测试实例中的方法是否已在mock中调用

sag*_*ism 9 python unit-testing mocking python-2.7

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

nuclear_reactor.py:

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

    def start(self):
        print "starting the nuclear reactor"
Run Code Online (Sandbox Code Playgroud)

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()
Run Code Online (Sandbox Code Playgroud)

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()
Run Code Online (Sandbox Code Playgroud)

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

manager creating the nuclear reactor
manager starting the nuclear reactor
manager starting the nuclear reactor
[call(), call().start(), call().start()]
0
Run Code Online (Sandbox Code Playgroud)

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

我可以使用依赖注入NuclearManager来传递一个模拟NuclearReactor,但我想有一种替代方法只使用mock.

Mar*_*ers 8

您确实在测试是否start直接在类上调用,而您的代码却没有.您可以直接在实例上测试该方法; 记住通过调用类生成实例:

print mock_reactor.return_value.calls
print mock_reactor.return_value.start.call_count
Run Code Online (Sandbox Code Playgroud)

Mock.return_value属性是对模拟类的调用的结果,因此是实例.

你也可以调用 mock.默认情况下,Mocks在调用时总是返回完全相同的对象,一个表示该返回值的新模拟:

print mock_reactor().calls
print mock_reactor().start.call_count
Run Code Online (Sandbox Code Playgroud)

调用mock实例和mock实例return_value属性的结果是同一个.

你已经在正确的道路上通过打印来电来NuclearReactor模拟,你只是错过了细节start()是在调用称为模拟,因此call().start(),不start()被记录下来.

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

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()
Run Code Online (Sandbox Code Playgroud)

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

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

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()
Run Code Online (Sandbox Code Playgroud)

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