[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