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