Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mock is not effective after twisted deferred is returned

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?

like image 933
Ajmal Ahammed Avatar asked Sep 11 '25 01:09

Ajmal Ahammed


2 Answers

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.

like image 74
Glyph Avatar answered Sep 13 '25 09:09

Glyph


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.

like image 41
Ajmal Ahammed Avatar answered Sep 13 '25 08:09

Ajmal Ahammed