使用两个模拟对象进行Python单元测试,如何验证调用顺序?

使用两个模拟对象进行Python单元测试,如何验证调用顺序?,python,unit-testing,mocking,Python,Unit Testing,Mocking,我正在编写一个类,它协调两个仪器(一个远程可控电源单元和一个用于控制被测设备的总线控制器),以便在被测设备(DUT)上执行各种测量 对这两个工具的访问都实现为Python类,新类可以使用对每个工具的引用。DUT有点脆弱,有一个非常特殊的通电顺序,包括对电源和总线控制器的调用,必须按照特定的顺序进行,以避免损坏DUT 现在我想为这个类编写一个单元测试。我目前正在为此使用鼻测试和模拟软件包。所以我的想法是模拟这两个仪器类,并验证它们的正确调用顺序 似乎很容易验证每个模拟类本身的调用顺序。因此,我能够

我正在编写一个类,它协调两个仪器(一个远程可控电源单元和一个用于控制被测设备的总线控制器),以便在被测设备(DUT)上执行各种测量

对这两个工具的访问都实现为Python类,新类可以使用对每个工具的引用。DUT有点脆弱,有一个非常特殊的通电顺序,包括对电源和总线控制器的调用,必须按照特定的顺序进行,以避免损坏DUT

现在我想为这个类编写一个单元测试。我目前正在为此使用鼻测试和模拟软件包。所以我的想法是模拟这两个仪器类,并验证它们的正确调用顺序

似乎很容易验证每个模拟类本身的调用顺序。因此,我能够找出电源是否被要求先正确地施加蓄电池电压,然后是数字域电压,然后是模拟域电压。我还可以发现数字寄存器已编程为正确的值。但是,我无法确定写入数字寄存器的调用是否发生在应用数字域电压和模拟域电压之间


所以我的问题是:如果我有两个模拟对象,如何验证这些对象之间调用的特定顺序?我的第一个想法是检查调用的时间戳,但这些时间戳似乎不存在。

您可以做的是将模拟放入一个新的
Mock()
对象中,并检查模拟对新模拟的调用。也许一个例子更容易理解:

>>> from unittest.mock import *
>>> m0, m1 = Mock(), Mock()
>>> m = Mock()
>>> m.m0, m.m1 = m0, m1
>>> m0()
<Mock name='mock.m0()' id='140660445334224'>
>>> m1()
<Mock name='mock.m1()' id='140660445334608'>
>>> m.mock_calls
[call.m0(), call.m1()]
因为我们希望第一次通过,而倒序失败


使用
patch
时,只需将补丁返回的Mock作为容器放入新的
Mock()
中即可。对记录子调用顺序的容器进行检查。

是否可以按顺序检查以下内容是否正确:既不调用A也不调用B;有人叫A,但B不叫;调用A和B?我认为可以在Mock上使用side_effect来启动另一个函数,该函数将报告您想要报告的任何内容,即将某些内容存储在全局列表或其他位置convenient@user3012759和我想的一样;例如,您甚至可以使用A的副作用来断言B未被调用。如果我不为了测试而更改代码,我只能在整个序列之前和之后执行操作。所以我不能保持在中间,检查是否已经完成了某些呼叫,而其他的还没有。我会研究使用副作用来达到我所需要的效果。是的,效果很好。谢谢对于那些感兴趣的人,autospec也适用于子模拟。@jan是的,一旦您创建了模拟,请将其设置为另一个模拟的子项,不要更改它。仅供参考,这对我不起作用。我一直收到一张空的电话单。我必须使用
attach\u mock
来正确跟踪呼叫,请参见以下回答:@moertel可能您正在尝试附加一个已经有父节点的mock。如果您查看一下,您可以看到
attach_mock()
只需在clean
mock
的父属性之后调用
setattr
。我在Python2和Python3的上一个
mock
版本中测试了它。首先创建父mock
m
,然后执行
m0,m1=m.m0,m.m1
,可能会更清晰。这将避免与附加模拟的混淆。
>>> m.assert_has_calls([call.m0(), call.m1()])
>>> m.assert_has_calls([call.m1(), call.m0()])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/mock.py", line 863, in assert_has_calls
    'Actual: %r' % (calls, self.mock_calls)
AssertionError: Calls not found.
Expected: [call.m1(), call.m0()]
Actual: [call.m0(), call.m1()]