为twisted trial测试模拟time.time()

0 投票
1 回答
2334 浏览
提问于 2025-04-18 09:46

我正在使用mock==1.0.1和Python版本2.7.3,并且使用twisted trial来运行测试。Twisted版本是13.2.0。

我正在为一个带有inlineCallbacks装饰器的函数编写模拟测试。这个函数使用了time模块中的time(),而我希望模拟这个时间,因为函数的返回值依赖于返回的时间。我想要验证返回值是否正确。

所以,我添加了一个@patch装饰器,但模拟测试没有被执行(在执行的测试列表中没有显示)。以下是代码 -

CURRENT_TIME = time.time()
@patch('my_module.time')
@defer.inlineCallbacks
def test_index(self, mock_time):
    mock_time.time.return_value = CURRENT_TIME
    handler = ClassToTest()
    result = yield handler.toTestFunc("")
    assertEqual(result, {"time":CURRENT_TIME, "values":4})

我的问题是,通过运行trial test.py来测试时,所有其他测试都能运行,除了test_index。当我注释掉patch装饰器时,测试可以运行并且会报错。我阅读了Where to patch部分,但仍然不知道如何模拟time.time()函数。我应该如何在测试中模拟time.time()函数呢?

1 个回答

3

与其使用模拟,不如参数化。

from time import time

class ClassToTest(object):
    def __init__(self, time=time):
        self._time = time

    def toTestFunc(self, arg):
        return {"time": self._time(), "values": 4}

def test_index(self):
    sut = ClassToTest(lambda: CURRENT_TIME)
    result = self.successResultOf(sut.toTestFunc(""))
    self.assertEqual({"time": CURRENT_TIME, "values": 4}, result)

在全局范围内进行猴子补丁(即修改现有代码)其实和参数化有点像,只是做得不太好。你并不需要替换整个程序的时间模块(甚至不需要替换一个模块里的所有代码)。你只需要在测试的单元中替换它。最简单的方法就是把它作为一个参数传递。

你可能还想做一些其他的事情。这些无论你是使用模拟、猴子补丁还是参数化都适用。

你想测试代码在没有被“动手脚”的情况下是否能正常工作。毕竟,这才是它在实际运行时的表现,而你希望它在那时能够正常工作。

from time import time

def test_default_time_function(self):
    sut = ClassToTest()
    before = time()
    result = self.successResultOf(sut.toTestFunc(""))
    after = time()
    # Possibly not ideal, but seems to work okay in practice and I haven't
    # had any better ideas for how to make assertions about `time()`
    self.assertTrue(before <= result["time"] <= after)

既然你在使用Twisted,你可能还想了解一下IReactorTime.seconds API和twisted.internet.task.Clock这个假对象。

from twisted.internet.task import Clock

class ClassToTest(object):
    def __init__(self, reactor):
        self._reactor = reactor

    def toTestFunc(self, arg):
        return {"time": self._reactor.seconds(), "values": 4}

def test_index(self):
    when = 123456.7
    reactor = Clock()
    reactor.advance(when)

    sut = ClassToTest(reactor)
    result = self.successResultOf(sut.toTestFunc(""))
    self.assertEqual({"time": when, "values": 4}, result)

在这个例子中,我没有提供默认的反应器。你可以提供,但通常明确传递反应器是个不错的主意,所以我一般不提供默认值。

撰写回答