From c57d53578a042e2a72d6928e8b81b2033e6045ce Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sat, 7 Jan 2017 00:09:50 +0900 Subject: [PATCH] Execute 'async def' test functions pytest didn't use the result of test functions but `async def` functions return an awaitable object. Though `async def` functions were just passed like they don't have any problem, their codes actually didn't run. For users, it was really hard to detect the tests really were run or not. This patch makes to check the result of test functions and if it is an awaitable object then actually run the functions to ensure its code is run. --- _pytest/compat.py | 7 +++++++ _pytest/python.py | 8 ++++++-- testing/test_async.py | 14 ++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 testing/test_async.py diff --git a/_pytest/compat.py b/_pytest/compat.py index d278b89cd44..a5d2e2be1f9 100644 --- a/_pytest/compat.py +++ b/_pytest/compat.py @@ -227,6 +227,13 @@ def _is_unittest_unexpected_success_a_failure(): return sys.version_info >= (3, 4) +if hasattr(inspect, 'isawaitable'): + isawaitable = inspect.isawaitable +else: + def isawaitable(f): + return False + + if _PY3: def safe_str(v): """returns v as string""" diff --git a/_pytest/python.py b/_pytest/python.py index e46f2f1bcfb..13853114ef8 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -19,7 +19,7 @@ isclass, isfunction, is_generator, _escape_strings, REGEX_TYPE, STRING_TYPES, NoneType, NOTSET, get_real_func, getfslineno, safe_getattr, - getlocation, enum, + getlocation, enum, isawaitable, ) cutdir1 = py.path.local(pluggy.__file__.rstrip("oc")) @@ -151,7 +151,11 @@ def pytest_pyfunc_call(pyfuncitem): testargs = {} for arg in pyfuncitem._fixtureinfo.argnames: testargs[arg] = funcargs[arg] - testfunction(**testargs) + testreturn = testfunction(**testargs) + if isawaitable(testreturn): + import asyncio + loop = asyncio.get_event_loop() + loop.run_until_complete(testreturn) return True def pytest_collect_file(path, parent): diff --git a/testing/test_async.py b/testing/test_async.py new file mode 100644 index 00000000000..8693e7a2f4d --- /dev/null +++ b/testing/test_async.py @@ -0,0 +1,14 @@ +import sys + +import pytest + + +@pytest.mark.skipif(sys.version_info < (3, 5), reason='async syntax available in Python 3.5+') +def test_async_function(testdir): + testdir.makepyfile(""" + async def test_async_function_py35(): + assert False + """) + # avoid importing asyncio into pytest's own process, which in turn imports logging (#8) + result = testdir.runpytest_subprocess() + result.stdout.fnmatch_lines(['*1 failed*'])