在 python 单元测试中模拟 class 和 class 方法
Mock a class and a class method in python unit tests
我正在使用 python 的 unittest.mock 在 Django 应用程序中进行一些测试。我想检查 class 是否被调用,并且其实例上的方法也被调用。
例如,给出这个简化的示例代码:
# In project/app.py
def do_something():
obj = MyClass(name='bob')
return obj.my_method(num=10)
这个测试来检查发生了什么:
# In tests/test_stuff.py
@patch('project.app.MyClass')
def test_it(self, my_class):
do_something()
my_class.assert_called_once_with(name='bob')
my_class.my_method.assert_called_once_with(num=10)
测试成功表明 my_class
被调用,但 my_class.my_method
未被调用。我知道我遗漏了一些东西——在被模拟的 class 上模拟一个方法? - 但我不确定是什么或如何让它发挥作用。
您的第二个模拟断言需要测试您是在 实例 上调用 my_method
,而不是 class 本身。
像这样调用模拟对象,
my_class().my_method.assert_called_once_with(num=10)
^^
这一行
my_class.my_method.assert_called_once_with(num=10)
如果 my_method
是 class 方法, 将起作用。
是这样吗?
否则,如果my_method
只是一个普通的实例方法,那么你将需要重构函数do_something
来获取实例变量obj
例如
def do_something():
obj = MyClass(name='bob')
return obj, obj.my_method(num=10)
# In tests/test_stuff.py
@patch('project.app.MyClass')
def test_it(self, my_class):
obj, _ = do_something()
my_class.assert_called_once_with(name='bob')
obj.my_method.assert_called_once_with(num=10)
对您的单元测试的一个小的重构建议,以帮助您处理您在测试中可能遇到的其他实例方法。您可以在 setUp
方法中设置所有这些,而不是在每个方法中模拟您的 class 。这样,通过模拟 class 并从 class 创建模拟对象,您现在可以根据需要多次使用该对象,测试 class 中的所有方法.
为了帮助说明这一点,我整理了以下示例。在线评论:
class MyTest(unittest.TestCase):
def setUp(self):
# patch the class
self.patcher = patch('your_module.MyClass')
self.my_class = self.patcher.start()
# create your mock object
self.mock_stuff_obj = Mock()
# When your real class is called, return value will be the mock_obj
self.my_class.return_value = self.mock_stuff_obj
def test_it(self):
do_something()
# assert your stuff here
self.my_class.assert_called_once_with(name='bob')
self.mock_stuff_obj.my_method.assert_called_once_with(num=10)
# stop the patcher in the tearDown
def tearDown(self):
self.patcher.stop()
为了深入了解这些是如何组合在一起的,在 setUp
方法中,我们将提供跨多个方法应用补丁的功能,如文档 here.[=18= 中所述]
补丁是在这两行中完成的:
# patch the class
self.patcher = patch('your_module.MyClass')
self.my_class = self.patcher.start()
最后,模拟对象在这里创建:
# create your mock object
self.mock_stuff_obj = Mock()
self.my_class.return_value = self.mock_stuff_obj
现在,您所有的测试方法都可以在所有调用中简单地使用 self.my_class
和 self.mock_stuff_obj
。
我正在使用 python 的 unittest.mock 在 Django 应用程序中进行一些测试。我想检查 class 是否被调用,并且其实例上的方法也被调用。
例如,给出这个简化的示例代码:
# In project/app.py
def do_something():
obj = MyClass(name='bob')
return obj.my_method(num=10)
这个测试来检查发生了什么:
# In tests/test_stuff.py
@patch('project.app.MyClass')
def test_it(self, my_class):
do_something()
my_class.assert_called_once_with(name='bob')
my_class.my_method.assert_called_once_with(num=10)
测试成功表明 my_class
被调用,但 my_class.my_method
未被调用。我知道我遗漏了一些东西——在被模拟的 class 上模拟一个方法? - 但我不确定是什么或如何让它发挥作用。
您的第二个模拟断言需要测试您是在 实例 上调用 my_method
,而不是 class 本身。
像这样调用模拟对象,
my_class().my_method.assert_called_once_with(num=10)
^^
这一行
my_class.my_method.assert_called_once_with(num=10)
如果 my_method
是 class 方法,将起作用。
是这样吗?
否则,如果my_method
只是一个普通的实例方法,那么你将需要重构函数do_something
来获取实例变量obj
例如
def do_something():
obj = MyClass(name='bob')
return obj, obj.my_method(num=10)
# In tests/test_stuff.py
@patch('project.app.MyClass')
def test_it(self, my_class):
obj, _ = do_something()
my_class.assert_called_once_with(name='bob')
obj.my_method.assert_called_once_with(num=10)
对您的单元测试的一个小的重构建议,以帮助您处理您在测试中可能遇到的其他实例方法。您可以在 setUp
方法中设置所有这些,而不是在每个方法中模拟您的 class 。这样,通过模拟 class 并从 class 创建模拟对象,您现在可以根据需要多次使用该对象,测试 class 中的所有方法.
为了帮助说明这一点,我整理了以下示例。在线评论:
class MyTest(unittest.TestCase):
def setUp(self):
# patch the class
self.patcher = patch('your_module.MyClass')
self.my_class = self.patcher.start()
# create your mock object
self.mock_stuff_obj = Mock()
# When your real class is called, return value will be the mock_obj
self.my_class.return_value = self.mock_stuff_obj
def test_it(self):
do_something()
# assert your stuff here
self.my_class.assert_called_once_with(name='bob')
self.mock_stuff_obj.my_method.assert_called_once_with(num=10)
# stop the patcher in the tearDown
def tearDown(self):
self.patcher.stop()
为了深入了解这些是如何组合在一起的,在 setUp
方法中,我们将提供跨多个方法应用补丁的功能,如文档 here.[=18= 中所述]
补丁是在这两行中完成的:
# patch the class
self.patcher = patch('your_module.MyClass')
self.my_class = self.patcher.start()
最后,模拟对象在这里创建:
# create your mock object
self.mock_stuff_obj = Mock()
self.my_class.return_value = self.mock_stuff_obj
现在,您所有的测试方法都可以在所有调用中简单地使用 self.my_class
和 self.mock_stuff_obj
。