使用Mox和Python测试模拟对象间的调用顺序

1 投票
2 回答
1240 浏览
提问于 2025-04-16 06:04

我正在测试一个函数,这个函数从一个辅助对象获取一个骨架对象,然后用第二个辅助对象对它进行修改,最后把修改后的对象传回第一个辅助对象。大概是这样的:

class ReadModifyUpdate(object):
    def __init__(self, store, modifier):
        self._store = store
        self._modifier = modifier

    def modify(key):
        record = self._store.read(key)
        self._modifier.modify(record)
        self._store.update(key, record)

我们可以用Python和Mox来测试这个:

class ReadModifyUpdateTest(mox.MoxTestBase):
    def test_modify(self):
        mock_record = self.mox.CreateMockAnthing()
        mock_store = self.mox.CreateMockAnything()
        mock_modifier = self.mox.CreateMockAnything()

        mock_store.read("test_key").AndReturn(mock_record)
        mock_modifier.modify(mock_record)
        mock_store.update("test_key", mock_record)
        self.mox.ReplayAll()

        updater = ReadModifyUpdate(mock_store, mock_modifier)
        updater.modify("test_key")

...但是这样并不能捕捉到一个错误,就是在调用modifier.modify()之前,store.update()被意外地调用了。在Mox中,有没有好的方法可以检查多个模拟对象上方法调用的顺序?类似于EasyMock的MocksControl对象?

2 个回答

0

这可能不是最好的解决办法,但你可以试着使用一个模拟对象,然后把它给你的测试对象用两次。这样你就可以控制调用的顺序了。

class ReadModifyUpdateTest(mox.MoxTestBase):
    def test_modify(self):
        mock_record = self.mox.CreateMockAnthing()
        mock_storeModifier = self.mox.CreateMockAnything()

        mock_storeModifier.read("test_key").AndReturn(mock_record)
        mock_storeModifier.modify(mock_record)
        mock_storeModifier.update("test_key", mock_record)
        self.mox.ReplayAll()

        updater = ReadModifyUpdate(mock_storeModifier, mock_storeModifier)
        updater.modify("test_key")
0

为了回答我自己的问题——我现在用一个副作用来检查调用顺序,这个方法可以正常工作。

我定义了一个辅助类:

class OrderedCallSequence(object):
    def __init__(self, test_case):
        self._expectation_count = 0
        self._evaluated = 0
        self._test_case = test_case

    def assertOrder(self):
        self._expectation_count += 1
        expected_position = self._expectation_count

        def side_effect(*args, **kwargs):
            self._evaluated += 1
            self._test_case.assertEquals(self._evaluated, expected_position,
                                         msg="Invoked in incorrect sequence")
        return side_effect

...测试案例变成了:

class ReadModifyUpdateTest(mox.MoxTestBase):
    def test_modify(self):
        mock_record = self.mox.CreateMockAnthing()
        mock_store = self.mox.CreateMockAnything()
        mock_modifier = self.mox.CreateMockAnything()

        sequence = OrderedCallSequence(self)
        mock_store.read("test_key").WithSideEffects(sequence.assertOrder()).AndReturn(mock_record)
        mock_modifier.modify(mock_record).WithSideEffects(sequence.assertOrder())
        mock_store.update("test_key", mock_record).WithSideEffects(sequence.assertOrder())
        self.mox.ReplayAll()

        updater = ReadModifyUpdate(mock_store, mock_modifier)
        updater.modify("test_key")

撰写回答