如何模拟不在测试范围内的方法?

5 投票
2 回答
2612 浏览
提问于 2025-04-18 09:47

如何模拟一个不在测试范围内的方法?

或者:如何模拟一个没有被直接调用的方法?

在这个例子中,我们要模拟的方法是baz
我正在使用来自pypi的Mock包

### tests
# ...
def test_method_a(self):
    # how to mock method that is called from bar() ?
    obj = foo.bar()
    self.assertEqual(obj.get('x'), 12345)

### foo
# ...
def bar():
    x = some_module.baz()  # <- how to mock baz() ?
    return x

2 个回答

0

在我看来,这个例子更好地展示了单元测试中的常见用法。

关键部分是 @patch 装饰器,它可以有效地将 side_effect 函数(这也可以是一个值)替换为被修补的函数。比较棘手的是,通常需要找到被修补函数的完整包路径。注意,你必须使用 '__main__.func' 来引用被修补的函数。

第一个测试 test_func 检查测试中的模拟值是否是原本预期的函数值('Hello World!')。第二个测试 test_mocked_funcfunc 修补为实际的函数对象 new_func,因此返回 True。第三个测试展示了如何用值替代新函数作为 side_effect。实际上,由于我们将替代值设为一个可迭代对象(side_effect=['New String 1!', 'New String 2!', 3]),每次运行时,它都会返回一个新值。

警告:如果你尝试调用修补后的函数的次数超过了你指定的返回值数量(在这个例子中是3),你会遇到 StopIteration 错误,因为你没有在 side_effect 中定义足够的返回值。

import unittest
from mock import patch # for Python 2.7
# from unittest.mock import patch # for Python 3.5

def func():
    return 'Hello World!'

def newFunc():
    return True

class TestFunc(unittest.TestCase):

    def test_func(self):
        self.assertEqual(func(), 'Hello World!')

    @patch('__main__.func', side_effect=newFunc)
    def test_mocked_func(self, *patches):
        self.assertTrue(func())

    @patch('__main__.func', side_effect=['New String 1!', 'New String 2!', 3])
    def test_mocked_func_again(self, *patches):
        self.assertEqual(func(), 'New String 1!')
        self.assertEqual(func(), 'New String 2!')
        self.assertEqual(func(), 3)
        # func() # This breaks the test because we only specified a list of length 3 in our patch.

if __name__=='__main__':
    unittest.main()
3

这里有一个例子,可以让你了解它是怎么工作的:

from mock import patch

def baz():
  return 'y'


def bar():
  x = baz()  # <- how to mock baz() ?
  return x


def test():
  with patch('__main__.baz') as baz_mock:
    baz_mock.return_value = 'blah'
    assert bar() == 'blah'
test()

撰写回答