[TIP] Testing asyncio coroutines
Stefan Scherfke
stefan at sofa-rockers.org
Wed Feb 18 23:58:53 PST 2015
> Am 2015-02-19 um 08:40 schrieb Ronny Pfannschmidt <opensource at ronnypfannschmidt.de>:
>
>
> the coroutine approach will be added to pytest after the 2.7 release
Cool. Let me know when I can help you with testing it. :-)
> On Tuesday, February 17, 2015 5:09:13 PM CEST, Stefan Scherfke wrote:
>> Hi all,
>>
>> I currently try to find a nice way to test asyncio coroutines.
>>
>> TL;DR: I found a possible solution, but I’m not sure if it’s correct or
>> too hacky.
>>
>> ---
>>
>> A naïve approach would be:
>>
>>
>> # tests/test_coros.py
>> import asyncio
>>
>> def test_coro():
>> loop = asyncio.get_event_loop()
>>
>> @asyncio.coroutine
>> def do_test():
>> yield from asyncio.sleep(0.1)
>> assert 0 # onoes!
>>
>> loop.run_until_complete(do_test())
>>
>>
>> I can also easily pull out getting a loop instance:
>>
>>
>> # tests/conftest.py
>> import asyncio
>>
>>
>> @pytest.yield_fixture
>> def loop():
>> loop = asyncio.new_event_loop()
>> asyncio.set_event_loop(loop)
>> yield loop
>> loop.close()
>>
>>
>> # tests/test_coros.py
>> def test_coro(loop):
>> @asyncio.coroutine
>> def do_test():
>> yield from asyncio.sleep(0.1)
>> assert 0 # onoes!
>>
>> loop.run_until_complete(do_test())
>>
>>
>> However, I actually want my tests to look like:
>>
>> # tests/test_coros.py
>> @asyncio.coroutine
>> def test_coro(loop):
>> yield from asyncio.sleep(0.1)
>> assert 0
>>
>>
>> I’ve come up with a (somewhat hacky?) solution:
>>
>> # tests/conftest.py
>> import asyncio
>>
>>
>> @pytest.yield_fixture
>> def loop():
>> loop = asyncio.new_event_loop()
>> asyncio.set_event_loop(loop)
>> yield loop
>> loop.close()
>>
>>
>> def pytest_pycollect_makeitem(collector, name, obj):
>> """Collect asyncio coroutines as normal functions, not as generators."""
>> if asyncio.iscoroutinefunction(obj):
>> return list(collector._genfunctions(name, obj))
>>
>>
>> def pytest_pyfunc_call(pyfuncitem):
>> """If ``pyfuncitem.obj`` is an asyncio coroutinefunction, execute it via
>> the event loop instead of calling it directly."""
>> testfunction = pyfuncitem.obj
>>
>> if not asyncio.iscoroutinefunction(testfunction):
>> return
>>
>> # Copied from _pytest/python.py:pytest_pyfunc_call()
>> funcargs = pyfuncitem.funcargs
>> testargs = {}
>> for arg in pyfuncitem._fixtureinfo.argnames:
>> testargs[arg] = funcargs[arg]
>> coro = testfunction(**testargs) # Will no execute the test yet!
>>
>> # Run the coro in the event loop
>> loop = testargs.get('loop', asyncio.get_event_loop())
>> loop.run_until_complete(coro)
>>
>> return True # TODO: What to return here?
>>
>>
>> It looks like it works normaly, that means I can use fixtutes in my
>> coroutine tests and if asserts fail, I get a correct report.
>>
>> I’m not sure though what to return from pytest_pyfunc_call(). Also,
>> I’m not sure if this is the intended way to solve this. What do you
>> think? What can I do better?
>>
>> Cheers,
>> Stefan
>>
>>
More information about the testing-in-python
mailing list