简体   繁体   English

在python单元测试中模拟一个类和一个类方法

[英]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_classself.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM