【问题标题】:Mock a class and a class method in python unit tests在python单元测试中模拟一个类和一个类方法
【发布时间】:2015-11-07 17:09:00
【问题描述】:

我正在使用 python 的 unittest.mock 在 Django 应用程序中进行一些测试。我想检查一个类是否被调用,以及它的实例上的一个方法是否也被调用。

例如,给定这个简化的示例代码:

# 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 没有被调用。我知道我错过了一些东西 - 在模拟类上模拟一个方法? - 但我不确定是什么或如何使它工作。

【问题讨论】:

    标签: python unit-testing mocking


    【解决方案1】:

    您的第二个模拟断言需要测试您在实例上调用my_method,而不是在类本身上。

    像这样调用模拟对象,

    my_class().my_method.assert_called_once_with(num=10)
            ^^
    

    【讨论】:

    • 噢!我花了很长时间研究我认为我在模拟中做错了什么,以至于我错过了那个愚蠢的错误!谢谢贾里德。
    【解决方案2】:

    为您的单元测试提供一个小的重构建议,以帮助您在测试中遇到其他实例方法。您可以在 setUp 方法中设置这一切,而不是在每个方法中模拟您的类。这样,通过模拟类并从该类创建一个模拟对象,您现在可以简单地使用该对象任意多次,测试您的类中的所有方法。

    为了帮助说明这一点,我整理了以下示例。在线评论:

    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 中所述。

    修补是在这两行中完成的:

        # patch the class
        self.patcher = patch('your_module.MyClass')
        self.my_class = self.patcher.start()
    

    最后在这里创建了mock对象:

        # create your mock object
        self.mock_stuff_obj = Mock()
        self.my_class.return_value = self.mock_stuff_obj
    

    现在,您的所有测试方法都可以在所有调用中简单地使用self.my_classself.mock_stuff_obj

    【讨论】:

    • 谢谢 idjaw。实际上我后来开始做一些类似的事情,但这仍然非常有用,谢谢!
    【解决方案3】:

    这一行

     my_class.my_method.assert_called_once_with(num=10)
    

    如果my_method 是一个类方法,则可以使用。

    是这样吗?

    否则,如果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)
    

    【讨论】:

    • 谢谢安东尼。这在某种程度上与 Jared 的答案不同/更好吗? (我不是说不是,我只是不够了解才知道!)
    猜你喜欢
    • 2017-05-12
    • 1970-01-01
    • 2018-03-14
    • 1970-01-01
    • 1970-01-01
    • 2020-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多