问题
I'm using Tornado as a coroutine engine for a periodic process, where the repeating coroutine calls ioloop.call_later()
on itself at the end of each execution. I'm now trying to drive this with unit tests (using Tornado's gen.test) where I'm mocking the ioloop's time with a local variable t:
DUT.ioloop.time = mock.Mock(side_effect= lambda: t)
(DUT <==> Device Under Test)
Then in the test, I manually increment t, and yield gen.moment
to kick the ioloop. The idea is to trigger the repeating coroutine after various intervals so I can verify its behaviour.
But the coroutine doesn't always trigger - or perhaps it yields back to the testing code before completing execution, causing failures.
I think should be using stop()
and wait()
to synchronise the test code, but I can't see concretely how to use them in this situation. And how does this whole testing strategy work if the DUT runs in its own ioloop?
回答1:
In general, using yield gen.moment
to trigger specific events is dicey; there are no guarantees about how many "moments" you must wait, or in what order the triggered events occur. It's better to make sure that the function being tested has some effect that can be asynchronously waited for (if it doesn't have such an effect naturally, you can use a tornado.locks.Condition
).
There are also subtleties to patching IOLoop.time
. I think it will work with the default Tornado IOLoops (where it is possible without the use of mock
: pass a time_func
argument when constructing the loop), but it won't have the desired effect with e.g. AsyncIOLoop
.
I don't think you want to use AsyncTestCase.stop
and .wait
, but it's not clear how your test is set up.
来源:https://stackoverflow.com/questions/33940518/unit-testing-a-periodic-coroutine-with-mock-time