为twisted trial测试模拟time.time()
我正在使用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)
在这个例子中,我没有提供默认的反应器。你可以提供,但通常明确传递反应器是个不错的主意,所以我一般不提供默认值。