I am having trouble with mocking in twisted trial unit test. The function under test returns a deferred, and I return that deferred from the unit test. However, it seems like the patches that I have applied are reverted once I return the deferred, it doesn't wait until the deferred is resolved.
Please see the following example:
schedule.py
import time
from twisted.internet import task, reactor
class Schedule():
def get_time(self):
t = time.time()
return task.deferLater(reactor, 2, lambda: t)
def get_time_future(self):
return task.deferLater(reactor, 2, lambda: time.time())
schedule_test.py
import mock
from twisted.trial import unittest
from schedule import Schedule
class ScheduleTest(unittest.TestCase):
@mock.patch('schedule.time')
def test_get_time(self, mock_time):
mock_time.time.return_value = 1450407660
d = Schedule().get_time()
d.addCallback(self.assertEqual, 1450407660)
return d
@mock.patch('schedule.time')
def test_get_time_future(self, mock_time):
mock_time.time.return_value = 1450407660
d = Schedule().get_time_future()
d.addCallback(self.assertEqual, 1450407660)
return d
And the output
test_schedule
ScheduleTest
test_get_time ... [OK]
test_get_time_future ... [FAIL]
===============================================================================
[FAIL]
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/twisted/trial/_synctest.py", line 437, in assertEqual
super(_Assertions, self).assertEqual(first, second, msg)
File "/usr/lib/python2.7/unittest/case.py", line 515, in assertEqual
assertion_func(first, second, msg=msg)
File "/usr/lib/python2.7/unittest/case.py", line 508, in _baseAssertEqual
raise self.failureException(msg)
twisted.trial.unittest.FailTest: 1456370638.190432 != 1450407660
test_schedule.ScheduleTest.test_get_time_future
-------------------------------------------------------------------------------
Ran 2 tests in 4.024s
FAILED (failures=1, successes=1)
Is there something wrong in the way that I am mocking time
in the above example?
There is indeed something wrong with the way you are mocking time
. The mock
decorator sets up the patches on entry to the test function and then removes them on exit. But, you're returning a Deferred
, which means that the time.time
callback runs well after the test function has exited.
If you just want to continue using a mock-like patching interface, you can use Twisted's TestCase.patch
, which I believe will take Deferreds
into account.
However, mocking is a testing anti-pattern, and a much better way to deal with the passage of time is to use the documented API for testing the passage of time, twisted.internet.task.Clock
.
As Glyph pointed out, using TestCase.patch
can help. The patch needs to be performed as shown below:
def test_get_time_future(self):
mock_time = MagicMock()
mock_time.time.return_value = 1450407660
self.patch(sys.modules['schedule'], 'time', mock_time)
d = Schedule().get_time_future()
d.addCallback(self.assertEqual, 1450407660)
return d
This test passes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With