Merge pull request #7144 from nicoddemus/async-testcase-7110

This commit is contained in:
Bruno Oliveira 2020-05-01 16:44:10 -03:00 committed by GitHub
commit e3643751bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 70 additions and 36 deletions

View File

@ -70,7 +70,7 @@ jobs:
- name: "windows-py38"
python: "3.8"
os: windows-latest
tox_env: "py38-twisted"
tox_env: "py38-unittestextras"
use_coverage: true
- name: "ubuntu-py35"

View File

@ -0,0 +1 @@
Fixed regression: ``asyncbase.TestCase`` tests are executed correctly again.

View File

@ -93,6 +93,13 @@ def iscoroutinefunction(func: object) -> bool:
return inspect.iscoroutinefunction(func) or getattr(func, "_is_coroutine", False)
def is_async_function(func: object) -> bool:
"""Return True if the given function seems to be an async function or async generator"""
return iscoroutinefunction(func) or (
sys.version_info >= (3, 6) and inspect.isasyncgenfunction(func)
)
def getlocation(function, curdir=None) -> str:
function = get_real_func(function)
fn = py.path.local(inspect.getfile(function))

View File

@ -34,8 +34,8 @@ from _pytest.compat import get_default_arg_names
from _pytest.compat import get_real_func
from _pytest.compat import getimfunc
from _pytest.compat import getlocation
from _pytest.compat import is_async_function
from _pytest.compat import is_generator
from _pytest.compat import iscoroutinefunction
from _pytest.compat import NOTSET
from _pytest.compat import REGEX_TYPE
from _pytest.compat import safe_getattr
@ -159,7 +159,7 @@ def pytest_configure(config):
)
def async_warn(nodeid: str) -> None:
def async_warn_and_skip(nodeid: str) -> None:
msg = "async def functions are not natively supported and have been skipped.\n"
msg += (
"You need to install a suitable plugin for your async framework, for example:\n"
@ -175,33 +175,13 @@ def async_warn(nodeid: str) -> None:
@hookimpl(trylast=True)
def pytest_pyfunc_call(pyfuncitem: "Function"):
testfunction = pyfuncitem.obj
try:
# ignoring type as the import is invalid in py37 and mypy thinks its a error
from unittest import IsolatedAsyncioTestCase # type: ignore
except ImportError:
async_ok_in_stdlib = False
else:
async_ok_in_stdlib = isinstance(
getattr(testfunction, "__self__", None), IsolatedAsyncioTestCase
)
if (
iscoroutinefunction(testfunction)
or (sys.version_info >= (3, 6) and inspect.isasyncgenfunction(testfunction))
) and not async_ok_in_stdlib:
async_warn(pyfuncitem.nodeid)
if is_async_function(testfunction):
async_warn_and_skip(pyfuncitem.nodeid)
funcargs = pyfuncitem.funcargs
testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}
result = testfunction(**testargs)
if hasattr(result, "__await__") or hasattr(result, "__aiter__"):
if async_ok_in_stdlib:
# todo: investigate moving this to the unittest plugin
# by a test call result hook
testcase = testfunction.__self__
testcase._callMaybeAsync(lambda: result)
else:
async_warn(pyfuncitem.nodeid)
async_warn_and_skip(pyfuncitem.nodeid)
return True

View File

@ -6,6 +6,7 @@ import traceback
import _pytest._code
import pytest
from _pytest.compat import getimfunc
from _pytest.compat import is_async_function
from _pytest.config import hookimpl
from _pytest.outcomes import exit
from _pytest.outcomes import fail
@ -227,13 +228,17 @@ class TestCaseFunction(Function):
self._needs_explicit_tearDown = True
raise _GetOutOf_testPartExecutor(exc)
setattr(self._testcase, self._testcase._testMethodName, wrapped_testMethod)
try:
self._testcase(result=self)
except _GetOutOf_testPartExecutor as exc:
raise exc.args[0] from exc.args[0]
finally:
delattr(self._testcase, self._testcase._testMethodName)
# let the unittest framework handle async functions
if is_async_function(self.obj):
self._testcase(self)
else:
setattr(self._testcase, self._testcase._testMethodName, wrapped_testMethod)
try:
self._testcase(result=self)
except _GetOutOf_testPartExecutor as exc:
raise exc.args[0] from exc.args[0]
finally:
delattr(self._testcase, self._testcase._testMethodName)
def _prunetraceback(self, excinfo):
Function._prunetraceback(self, excinfo)

View File

@ -1,7 +1,13 @@
from unittest import IsolatedAsyncioTestCase # type: ignore
teardowns = []
class AsyncArguments(IsolatedAsyncioTestCase):
async def asyncTearDown(self):
teardowns.append(None)
async def test_something_async(self):
async def addition(x, y):
return x + y
@ -13,3 +19,6 @@ class AsyncArguments(IsolatedAsyncioTestCase):
return x + y
self.assertEqual(await addition(2, 2), 3)
def test_teardowns(self):
assert len(teardowns) == 2

View File

@ -0,0 +1,22 @@
"""Issue #7110"""
import asyncio
import asynctest
teardowns = []
class Test(asynctest.TestCase):
async def tearDown(self):
teardowns.append(None)
async def test_error(self):
await asyncio.sleep(0)
self.fail("failing on purpose")
async def test_ok(self):
await asyncio.sleep(0)
def test_teardowns(self):
assert len(teardowns) == 2

View File

@ -1136,4 +1136,13 @@ def test_async_support(testdir):
testdir.copy_example("unittest/test_unittest_asyncio.py")
reprec = testdir.inline_run()
reprec.assertoutcome(failed=1, passed=1)
reprec.assertoutcome(failed=1, passed=2)
def test_asynctest_support(testdir):
"""Check asynctest support (#7110)"""
pytest.importorskip("asynctest")
testdir.copy_example("unittest/test_unittest_asynctest.py")
reprec = testdir.inline_run()
reprec.assertoutcome(failed=1, passed=2)

View File

@ -10,7 +10,7 @@ envlist =
py37
py38
pypy3
py37-{pexpect,xdist,twisted,numpy,pluggymaster}
py37-{pexpect,xdist,unittestextras,numpy,pluggymaster}
doctesting
py37-freeze
docs
@ -49,7 +49,8 @@ deps =
pexpect: pexpect
pluggymaster: git+https://github.com/pytest-dev/pluggy.git@master
pygments
twisted: twisted
unittestextras: twisted
unittestextras: asynctest
xdist: pytest-xdist>=1.13
{env:_PYTEST_TOX_EXTRA_DEP:}