bpo-32972: Add unittest.AsyncioTestCase by dave-shawley · Pull Request #10296 · python/cpython (original) (raw)
This PR adds co-routine support to unittest by adding a new sub-class of unittest.TestCase
named unittest.AsyncioTestCase
. It extends unittest.TestCase
by adding the following functionality:
- a new event loop is created and terminated with each test method & the same event loop is used throughout the entire test method execution from setup through cleanup
- asynchronous test setup and teardown are supported by new methods -
asyncSetUp
andasyncTearDown
- test methods that are decorated with the
async
keyword are run on the event loop. Undecorated methods are run as normal methods. - cleanup hooks that are decorated with the
async
keyword are run on the event loop. Undecorated methods are run as normal methods.
I made the following conscious decisions during the implementation that may be somewhat controversial so I want to mention them here (in no particular order) along with the rationale.
- there is no direct support for other event loop implementation: other event loop implementations can be plugged in using a custom EventLoopPolicy implementation. It can be inserted by overriding
setUpClass
. I believe that other loop implementations can either plug in using the event loop policy or provide their own asynchronous test case implementation (e.g., tornado.testing). - asyncio.iscoroutine is used to detect async methods: the two places that I need to detect whether a callable is a co-routine or not are: (a) test methods and (b) cleanup hooks added with addCleanup. Both are explicit (opt-in) actions so they are completely controlled by the test writer.
- outstanding asyncio Tasks are not terminated: I'm still on the fence about whether the test should fail if there are outstanding tasks or if it should gather them.
- async test case is a separate class: I decided to use a sub-class so that it is immediately obvious that a test methods are intended to run on the event loop. This also insulates
unittest.TestCase
from unexpected breakages and maintains it's current behavior.
Open Questions
- What should happen when
len(asyncio.all_tasks())
is non-zero after cleanups are run? - Should
unittest.AsyncioTestCase
include a global test run timeout?
I still have to amend commits to update documentation but I wanted to have the discussion about the implementation choices before I started writing up examples and updating the existing documentation suite.