使用python';s mock patch.object更改在另一个方法中调用的方法的返回值

使用python';s mock patch.object更改在另一个方法中调用的方法的返回值,python,unit-testing,mocking,patch,Python,Unit Testing,Mocking,Patch,是否可以模拟在我尝试测试的另一个函数中调用的函数的返回值?我希望mock方法(在我测试的许多方法中都会调用)在每次调用时都返回我指定的变量。例如: class Foo: def method_1(): results = uses_some_other_method() def method_n(): results = uses_some_other_method() 在单元测试中,我想使用mock来更改uses_some_other_method

是否可以模拟在我尝试测试的另一个函数中调用的函数的返回值?我希望mock方法(在我测试的许多方法中都会调用)在每次调用时都返回我指定的变量。例如:

class Foo:
    def method_1():
       results = uses_some_other_method()
    def method_n():
       results = uses_some_other_method()

在单元测试中,我想使用mock来更改
uses_some_other_method()
的返回值,以便在
Foo
中调用它时,它将返回我在
@patch.object(…)

中定义的内容。可以通过以下方式完成:

# foo.py
class Foo:
    def method_1():
        results = uses_some_other_method()


# testing.py
from mock import patch

@patch('Foo.uses_some_other_method', return_value="specific_value"):
def test_some_other_method(mock_some_other_method):
    foo = Foo()
    the_value = foo.method_1()
    assert the_value == "specific_value"

这里有一个你可以阅读的资料来源:

有两种方法可以做到这一点;使用patch和patch.object

补丁假定您不是直接导入对象,而是由正在测试的对象使用,如下所示

#foo.py
def some_fn():
    return 'some_fn'

class Foo(object):
    def method_1(self):
        return some_fn()
如果您直接导入要测试的模块,则可以使用patch.object,如下所示:

#test_case_2.py
import foo
from mock import patch

@patch.object(foo, 'some_fn')
def test_foo(test_some_fn):
    test_some_fn.return_value = 'test-val-1'
    tmp = foo.Foo()
    assert tmp.method_1() == 'test-val-1'
    test_some_fn.return_value = 'test-val-2'
    assert tmp.method_1() == 'test-val-2'
在这两种情况下,在测试功能完成后,某些_fn将被“取消模拟”

编辑: 为了模拟多个函数,只需向函数中添加更多的装饰器,并添加参数以接受额外的参数

@patch.object(foo, 'some_fn')
@patch.object(foo, 'other_fn')
def test_foo(test_other_fn, test_some_fn):
    ...

请注意,装饰器离函数定义越近,它在参数列表中的位置就越早。

让我澄清一下你在说什么:你想在一个测试用例中测试
Foo
,这个测试用例调用外部方法
使用一些其他方法。您希望模拟返回值,而不是调用实际的方法

class Foo:
    def method_1():
       results = uses_some_other_method()
    def method_n():
       results = uses_some_other_method()
假设上面的代码在
foo.py
中,并且
使用了一些其他方法
在模块
bar.py
中定义。以下是单元测试:

import unittest
import mock

from foo import Foo


class TestFoo(unittest.TestCase):

    def setup(self):
        self.foo = Foo()

    @mock.patch('foo.uses_some_other_method')
    def test_method_1(self, mock_method):
        mock_method.return_value = 3
        self.foo.method_1(*args, **kwargs)

        mock_method.assert_called_with(*args, **kwargs)

如果您想在每次传入不同参数时更改返回值,
mock
提供了。

要添加到Silfheed的答案中(这很有用),我需要修补所讨论对象的多个方法。我发现这样做更优雅:

给定以下要测试的函数,位于
模块.a\u function.to\u test.py

from some_other.module import SomeOtherClass

def add_results():
    my_object = SomeOtherClass('some_contextual_parameters')
    result_a = my_object.method_a()
    result_b = my_object.method_b()
    
    return result_a + result_b
要测试此函数(或类方法,这无关紧要),可以使用
patch.object()
结合
sys.modules
来修补类
SomeOtherClass
的多个方法:

@patch.object(sys.modules['module.a_function.to_test'], 'SomeOtherClass')
def test__should_add_results(self, mocked_other_class):
  mocked_other_class().method_a.return_value = 4
  mocked_other_class().method_b.return_value = 7

  self.assertEqual(add_results(), 11)
无论您需要修补的
SomeOtherClass
的方法有多少,这都是有效的,并且会产生独立的结果

此外,使用相同的修补方法,如果需要,可以返回
SomeOtherClass
的实际实例:

@patch.object(sys.modules['module.a_function.to_test'], 'SomeOtherClass')
def test__should_add_results(self, mocked_other_class):
  other_class_instance = SomeOtherClass('some_controlled_parameters')
  mocked_other_class.return_value = other_class_instance 
  ...

感谢您解释patch和patch.object之间的区别。如果foo是一个我无法访问的库,我想模拟的是对方法_1的调用,该怎么办?回答很好,内容丰富,解释清楚。非常感谢您的这句话:装饰器离函数定义越近,它在参数列表中的位置就越早。“只花了1个小时调试这个…@LanaNova这里也是。我希望参数的顺序类似于patch.object列出它们的方式。作为参考,任何人都可以参考这篇叙述得很好的文章,非常感谢。你提到的那篇文章真的很有帮助。
@patch.object(sys.modules['module.a_function.to_test'], 'SomeOtherClass')
def test__should_add_results(self, mocked_other_class):
  mocked_other_class().method_a.return_value = 4
  mocked_other_class().method_b.return_value = 7

  self.assertEqual(add_results(), 11)
@patch.object(sys.modules['module.a_function.to_test'], 'SomeOtherClass')
def test__should_add_results(self, mocked_other_class):
  other_class_instance = SomeOtherClass('some_controlled_parameters')
  mocked_other_class.return_value = other_class_instance 
  ...