[英]Mock a class and a class method in python unit tests
I'm using python's unittest.mock to do some testing in a Django app. 我正在使用python的unittest.mock在Django应用程序中进行一些测试。 I want to check that a class is called, and that a method on its instance is also called.
我想检查一个类是否被调用,并且还调用了它的实例上的方法。
For example, given this simplified example code: 例如,给出这个简化的示例代码:
# In project/app.py
def do_something():
obj = MyClass(name='bob')
return obj.my_method(num=10)
And this test to check what's happening: 这个测试来检查发生了什么:
# 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)
The test successfully says that my_class
is called, but says my_class.my_method
isn't called. 测试成功地说调用了
my_class
,但是没有调用my_class.my_method
。 I know I'm missing something - mocking a method on the mocked class? 我知道我错过了一些东西 - 在模拟类上嘲笑一个方法? - but I'm not sure what or how to make it work.
- 但我不确定是什么或如何使它工作。
Your second mock assertion needs to test that you are calling my_method
on the instance , not on the class itself. 你的第二个模拟断言需要测试你是在实例上调用
my_method
,而不是在类本身上调用my_method
。
Call the mock object like this, 像这样调用模拟对象,
my_class().my_method.assert_called_once_with(num=10)
^^
A small refactoring suggestion for your unittests to help with other instance methods you might come across in your tests. 对您的单元测试的一个小的重构建议,以帮助您在测试中可能遇到的其他实例方法。 Instead of mocking your class in each method, you can set this all up in the
setUp
method. 您可以在
setUp
方法中设置全部,而不是在每个方法中模拟您的类。 That way, with the class mocked out and creating a mock object from that class, you can now simply use that object as many times as you want, testing all the methods in your class. 这样,当类被模拟并从该类创建一个模拟对象时,您现在可以根据需要多次使用该对象,测试类中的所有方法。
To help illustrate this, I put together the following example. 为了帮助说明这一点,我将以下示例放在一起。 Comments in-line:
评论内联:
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()
To provide some insight on how this is put together, inside the setUp
method we will provide functionality to apply the patch across multiple methods as explained in the docs here . 为了提供关于这是如何放在一起的一些见解,里面
setUp
方法,我们将提供功能能够跨多个方法应用补丁作为文档解释这里 。
The patching is done in these two lines: 修补程序在以下两行中完成:
# patch the class
self.patcher = patch('your_module.MyClass')
self.my_class = self.patcher.start()
Finally, the mock object is created here: 最后,在这里创建模拟对象:
# create your mock object
self.mock_stuff_obj = Mock()
self.my_class.return_value = self.mock_stuff_obj
Now, all your test methods can simply use self.my_class
and self.mock_stuff_obj
in all your calls. 现在,您的所有测试方法都可以在所有调用中使用
self.my_class
和self.mock_stuff_obj
。
This line 这条线
my_class.my_method.assert_called_once_with(num=10)
will work if my_method
is a class method. 如果
my_method
是一个类方法,它将起作用。
Is it the case? 是这样的吗?
Otherwise, if my_method
is just an normal instance method, then you will need to refactor the function do_something
to get hold of the instance variable obj
否则,如果
my_method
只是一个普通的实例方法,那么你需要重构函数do_something
以获取实例变量obj
eg 例如
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)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.