[Twisted-Python] Unit testing, trail, inlineCallbacks, deferreds and mocking

Adi Roiban adi at roiban.ro
Tue Jan 27 08:18:12 MST 2015


On 27 January 2015 at 13:00, Patryk Ściborek <patryk at sciborek.com> wrote:
> Hi!
>
> I've just started a new project using Twisted and I want to write unit tests
> since the beginning. Unfortunately I've got some trouble understanding how
> should I do it. I read 'Test-driven development with Twisted', read some
> articles on the web and searched on the mailing list but I couldn't find
> anything which make it clear for me.
>
> I've got a class:
>
> class SessionCleaner(object):
>     def __init__(self, session_db, interval=10):
>         self.session_db = session_db
>         self.lc = task.LoopingCall(self.check_old_sessions)

def __init__(self, session_db, interval=10, reactor=reactor)
    self.lc = task.LoopingCall(self.check_old_sessions)
    self.lc.clock = reactor

Then in tests you can replace the default reactor with
twisted.internet.task.Clock
In this way you have control over the looping call.

>         self.lc.start(interval)
>
>     @defer.inlineCallbacks
>     def check_old_sessions(self):
>         log.msg('check_old_sessions()', logLevel=logging.DEBUG)
>         try:
>             old_sessions = yield self.session_db.get_old_sessions()
>             for s in old_sessions:
>                 yield self.session_db.process_stopped(s)
>
>         except txredisapi.ConnectionError as e:
>             log.msg('check_old_sessions - connection error {}'
>                     .format(e), logLevel=logging.WARNING)
>
> session_db is a object with methods which makes some calls to Redis.

In tests you can replace session_db with an InMemorySessionDB which is
rigged to return on demand a failure or success.

class InMemorySessionDB(object):

    def __init__(self):
         self._session = []
         self._stop_session = {}

    def addRiggedSession(self, session, stop_result):
          self._session.append(session)
          self._stop_session[session] = stop_result

    def get_old_sessions(self)
        return self._session

   def process_stopped(self, session):
      return self._stop_session[session]

InMemorySessionDB can also inherit form the real deal class and just
overwrite exit points.

This is more like a mock with a spec.. as it will fail if arbitrary
methods are called with arbitrary arguments.

I don't think there is a right way of doing it, but I try to avoid
using generic Mock or MagicMock objects.

Good luck!
-- 
Adi Roiban




More information about the Twisted-Python mailing list