Saturday 4:30 p.m.–5 p.m.
Eventually Correct: Testing Async Apps
- Audience level:
Async frameworks like Tornado and asyncio scramble our usual strategies for writing sequential code. This is most problematic when writing tests: how can you validate the outcome when you don’t know when to expect it? This talk introduces you to methods and practices for unittesting async applications.
**The problem:** async applications must be tested as thoroughly as others, but the typical code flow is broken. “Do something, then assert something” no longer works in the obvious way. After your test code launches an async operation, it needs some way to know when to expect the operation to be complete. Developers inexperienced with async frameworks insert arbitrary sleeps into their tests to wait for the expected outcome, or they write awkward polling loops. The resulting code is unreliable, breaks good testing practices, and is illegible. There has to be a better way. **The fetch / wait pattern.** Tornado’s “testing” module introduced a pattern that allows async web applications to be easily tested. Your code does some setup and then fetches a URL from your application. Tornado then allows your test to wait cleanly, while the event loop completes processing your request. Finally, your test asserts the post-conditions. **Loop management.** The event loop must be shut down and recreated between tests to ensure all file descriptors and callbacks are cleared between tests. We’ll see how to manage the loop when testing with Tornado and with asyncio. **Coroutine tests.** Tornado’s “gen_test” decorator allows you to write tests as generators. We digress to explain asynchronous coroutines in general, then show how they vastly improve async tests’ concision and reliability. Tornado helps you avoid some pitfalls with a few neat tricks, we’ll see how those work. Then we see equivalent examples of testing asyncio applications with coroutines. **Don’t sleep.** Sometimes a test requires time to pass, but calling time.sleep() in a test is slow, unreliable, and may block the event loop precisely when it needs to be running. The self-tests for asyncio demonstrate some clever patterns to simulate the passage of time while ensuring tests are fast and trustworthy. **Conclusion.** As async frameworks make their way into the mainstream of Python application programming, we must take the experience we’ve gained with conventional testing and adapt it to a new paradigm. Don’t reinvent the wheel: there are years of wisdom embedded in Tornado’s and asyncio’s testing tools. You can write fast, reliable, and elegant async tests if you use the right techniques.