[Twisted-Python] Re: Simulating time for reactor

David Bolen db3l at fitlinxx.com
Tue Jan 13 10:42:46 EST 2004

Christopher Armstrong <radix at twistedmatrix.com> writes:

> That's your incorrect assumption; you can stop
> reactors. reactor.crash().

Well, actually I'm well aware of being able to stop reactors.  It's
just that doing so isn't a good fit for my current test scenario.

>                            Usually one does this from some callback
> within their test.

Ah, there's your incorrect assumption :-) That the reactor delayed
events that are created by the code under test end up in a callback
within the test itself.  If that were the case I might not have a
problem using reactor.run() and reactor.stop(), although I suppose I'd
still have to think about the case when the callback was incorrectly
never called - how would the test terminate in that case with a
failure?  (Guess I'd have to install an additional callLater as a
back-stop or something, which is unnecessary latency in a failure
scenario - could easily add up over a lot of tests)

>                    I've written tests with both iterate() and
> run()/crash(); the latter is usually more natural to me. I don't
> remember enough right now to try to make a blanket assertion about
> which one is better, though (maybe I should write more tests again ;).

I'm definitely not saying which is better or worse, but in the tests I
was referring to, iterate() was clearly more applicable.  I think I
could see using run()/crash() if I were testing a more protocol piece
of code, but that's not my current scenario, where it's primarily
delayed calls I want control of.

As an example, I'm testing the controller portion of an MVC setup.
Most of the tests initialize a model to some known state, assign it to
a controller, and then initiate certain events through the controller.
The controller will be part of a bigger Twisted-based system, but the
controller itself handles no network I/O, so the only part of Twisted
it's really depending on is the IReactorTime for delayed actions used
as timeouts in the controller.  The checks in the test are that the
model ends up in the right final state after the events.  I don't care
how the controller accomplishes that feat (it's internals are opaque
to users and semi-opaque to tests), just that the model ends up the
right way.

Some of the model changes are triggered by internal timeouts within
the controller, so to test that, the test adjusts the timeout value
used by the controller (hence "semi-opaque") when establishing delayed
calls so that I know the call - if the controller set it up - will
fire during the next iterate().  I can then sequence the iterate()
call along with other events to simulate various permutations of
timing of events with respect to the delayed call occurring.  To my
mind it's extremely clean and controlled given that I'm effectively
testing asynchronous events with respect to specific use cases.  That
is, except for externally overriding the timer value, which is only
needed to ensure the delayed call will trigger when I want it to (thus
my request here).

But with respect to reactor.run(), the key is that the callback for
the delayed call is internal to the controller and the test is not
interfering with that at all, nor does it have visibility to it.  I
could theoretically subclass the controller as a mock controller to
add a reactor.stop() to such a callback, but that would be much more
complexity to me than simply using iterate() when appropriate in the
test.  I think both have their place, but I definitely like the
availability of iterate() for this scenario.

-- David

More information about the Twisted-Python mailing list