测试实例中的方法是否已在模拟中被调用 [英] Testing that a method in instance has been called in mock

查看:83
本文介绍了测试实例中的方法是否已在模拟中被调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这种设置,我正在测试使用另一个类的一个类,并且我想模拟后者,所以我只测试第一个类本身.

I have this sort of setup where I'm testing a class which is using another class, and I want to mock the latter so I'm only testing the first class itself.

nuclear_reactor.py:

class NuclearReactor():
    def __init__(self):
        print "initializing the nuclear reactor"

    def start(self):
        print "starting the nuclear reactor"

nuclear_manager.py:

from nuclear_reactor import NuclearReactor

class NuclearManager():
    def __init__(self):
        print "manager creating the nuclear reactor"
        self.reactor = NuclearReactor()

    def start(self):
        print "manager starting the nuclear reactor"
        self.reactor.start()

test_nuclear_manager.py:

from mock import Mock
import nuclear_manager
from nuclear_manager import NuclearManager

def test():
    mock_reactor = nuclear_manager.NuclearReactor = Mock()
    nuke = NuclearManager()
    nuke.start()
    nuke.start()
    print mock_reactor.mock_calls
    print mock_reactor.start.call_count

test()

我要测试的是NuclearReactor.start被调用, 但是当我运行它时,我得到:

What I'd like to test is that NuclearReactor.start is called, but when I run this I get:

manager creating the nuclear reactor
manager starting the nuclear reactor
manager starting the nuclear reactor
[call(), call().start(), call().start()]
0

我完全理解,因为start是实例的属性而不是类的属性,并且我可以解析mock_calls,但是没有更好的方法来检查实例化的模拟类的调用是制成的?

Which I totally understand since start is an attribute of the instance and not of the class, and I could parse the mock_calls, but isn't there a better way to check that the call of an instantiated mocked class is made?

我可以在NuclearManager中使用依赖项注入来传递模拟NuclearReactor,但是我认为有一种使用模拟的替代方法.

I could use dependency injection in NuclearManager to pass a mock NuclearReactor, but I'm thinking there would be an alternative way using just mock.

推荐答案

您确实正在测试start是否已在类上直接调用 ,而您的代码并未如此.您可以直接在实例上测试方法;请记住,实例是通过调用类产生的:

You are indeed testing if start has been called directly on the class, which your code does not. You can test the method on the instance directly; remember that an instance is produced by calling the class:

print mock_reactor.return_value.calls
print mock_reactor.return_value.start.call_count

Mock.return_value属性是对模拟类(即实例)的调用的结果.

The Mock.return_value attribute is the result of the call to the mocked class, so the instance.

您也可以调用该模拟游戏.默认情况下,模拟程序总是在调用时返回完全相同的对象,即代表该返回值的新模拟程序:

You can also just call the mock. Mocks by default always return the exact same object when called, a new mock representing that return value:

print mock_reactor().calls
print mock_reactor().start.call_count

调用模拟实例的结果和模拟实例的return_value属性是相同的.

The result of calling a mock instance, and the mock instance return_value attribute, are one and the same.

通过打印对NuclearReactor模拟的调用,您已经处在正确的路径上,您只是错过了start()在称为模拟中调用的细节,所以call().start(),没有start()被记录.

You were already on the right path by printing out the calls to the NuclearReactor mock, you just missed the detail that start() was invoked on the called mock, so call().start(), not start() was recorded.

您可能要使用 mock.patch() 处理补丁,而不是直接分配;这样可以确保再次删除补丁程序 ,以便其他测试可以自己决定要模拟的内容:

You may want to use mock.patch() to handle the patching, rather than by direct assignment; this makes sure that the patch is removed again so that other tests can make their own decisions on what is mocked:

import mock
from nuclear_manager import NuclearManager

@mock.patch('nuclear_manager.NuclearReactor')
def test(mock_reactor):
    nuke = NuclearManager()
    nuke.start()
    nuke.start()

    instance = mock_reactor.return_value
    assert instance.start.call_count == 2
    instance.assert_called()

我在这里用它作为装饰器;当test()函数被调用时,该模拟被放置在适当的位置,而当该函数退出时,它将被再次删除.您还可以使用patch()作为上下文管理器来更好地限制补丁的范围.

I used it as a decorator here; when the test() function is called, the mock is put in place, and when the function exits, it is removed again. You can also use patch() as a context manager to limit the scope of the patch even more finely.

另外,对于这样的单元测试,请使用 unittest:

Also, for unit testing like this, do use the unittest library:

import mock
import unittest
import nuclear_manager

class NuclearManagerTests(unittest.TestCase):
    @mock.patch('nuclear_manager.NuclearReactor')
    def test_start(self, mock_reactor):
        nuke = NuclearManager()
        nuke.start()
        nuke.start()

        instance = mock_reactor.return_value
        self.assertEqual(instance.start.call_count, 2)
        instance.assert_called()

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

这使您可以将测试适合更大的测试套件,启用和禁用测试以及与其他测试工具集成.

This lets you fit your tests into a larger test suite, enable and disable tests, and integrate with other testing tools.

这篇关于测试实例中的方法是否已在模拟中被调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆