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



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

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(...) 中定义的内容

有两种方法可以做到这一点;带补丁和带补丁对象

Patch假设您没有直接导入对象,但它正被您正在测试的对象使用,如下所示

#foo.py
def some_fn():
    return 'some_fn'
class Foo(object):
    def method_1(self):
        return some_fn()
#bar.py
import foo
class Bar(object):
    def method_2(self):
        tmp = foo.Foo()
        return tmp.method_1()
#test_case_1.py
import bar
from mock import patch
@patch('foo.some_fn')
def test_bar(mock_some_fn):
    mock_some_fn.return_value = 'test-val-1'
    tmp = bar.Bar()
    assert tmp.method_2() == 'test-val-1'
    mock_some_fn.return_value = 'test-val-2'
    assert tmp.method_2() == 'test-val-2'

如果你直接导入要测试的模块,你可以使用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'

在这两种情况下,some_fn都将在测试函数完成后被"取消模拟"。

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

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

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

这可以用这样的东西来完成:

# 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"

这里有一个你可以阅读的来源:在错误的地方修补

让我澄清一下您所说的内容:您希望在一个调用外部方法uses_some_other_method的测试用例中测试Foo。您希望模拟返回值,而不是调用实际的方法。

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

假设上述代码在foo.py中,并且uses_some_other_method在模块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提供side_effect

为了增加Silfheed的答案(这很有用),我需要修补有问题对象的多个方法。我发现这样做更优雅:

给定以下要测试的功能,位于module.a_function.to_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 
  ...

最新更新