Emit a warning when a async def function is not handled by a plugin
Fix #2224
This commit is contained in:
		
							parent
							
								
									90597226eb
								
							
						
					
					
						commit
						40072b9511
					
				| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					``async`` test functions are skipped and a warning is emitted when a suitable
 | 
				
			||||||
 | 
					async plugin is not installed (such as ``pytest-asyncio`` or ``pytest-trio``).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Previously ``async`` functions would not execute at all but still be marked as "passed".
 | 
				
			||||||
| 
						 | 
					@ -43,6 +43,7 @@ from _pytest.mark import MARK_GEN
 | 
				
			||||||
from _pytest.mark.structures import get_unpacked_marks
 | 
					from _pytest.mark.structures import get_unpacked_marks
 | 
				
			||||||
from _pytest.mark.structures import normalize_mark_list
 | 
					from _pytest.mark.structures import normalize_mark_list
 | 
				
			||||||
from _pytest.outcomes import fail
 | 
					from _pytest.outcomes import fail
 | 
				
			||||||
 | 
					from _pytest.outcomes import skip
 | 
				
			||||||
from _pytest.pathlib import parts
 | 
					from _pytest.pathlib import parts
 | 
				
			||||||
from _pytest.warning_types import PytestWarning
 | 
					from _pytest.warning_types import PytestWarning
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -156,6 +157,15 @@ def pytest_configure(config):
 | 
				
			||||||
@hookimpl(trylast=True)
 | 
					@hookimpl(trylast=True)
 | 
				
			||||||
def pytest_pyfunc_call(pyfuncitem):
 | 
					def pytest_pyfunc_call(pyfuncitem):
 | 
				
			||||||
    testfunction = pyfuncitem.obj
 | 
					    testfunction = pyfuncitem.obj
 | 
				
			||||||
 | 
					    iscoroutinefunction = getattr(inspect, "iscoroutinefunction", None)
 | 
				
			||||||
 | 
					    if iscoroutinefunction is not None and iscoroutinefunction(testfunction):
 | 
				
			||||||
 | 
					        msg = "Coroutine 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"
 | 
				
			||||||
 | 
					        msg += "  - pytest-asyncio\n"
 | 
				
			||||||
 | 
					        msg += "  - pytest-trio\n"
 | 
				
			||||||
 | 
					        msg += "  - pytest-tornasync"
 | 
				
			||||||
 | 
					        warnings.warn(PytestWarning(msg.format(pyfuncitem.nodeid)))
 | 
				
			||||||
 | 
					        skip(msg="coroutine function and no async plugin installed (see warnings)")
 | 
				
			||||||
    funcargs = pyfuncitem.funcargs
 | 
					    funcargs = pyfuncitem.funcargs
 | 
				
			||||||
    testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}
 | 
					    testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}
 | 
				
			||||||
    testfunction(**testargs)
 | 
					    testfunction(**testargs)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1179,3 +1179,31 @@ def test_fixture_mock_integration(testdir):
 | 
				
			||||||
def test_usage_error_code(testdir):
 | 
					def test_usage_error_code(testdir):
 | 
				
			||||||
    result = testdir.runpytest("-unknown-option-")
 | 
					    result = testdir.runpytest("-unknown-option-")
 | 
				
			||||||
    assert result.ret == EXIT_USAGEERROR
 | 
					    assert result.ret == EXIT_USAGEERROR
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@pytest.mark.skipif(
 | 
				
			||||||
 | 
					    sys.version_info[:2] < (3, 5), reason="async def syntax python 3.5+ only"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					@pytest.mark.filterwarnings("default")
 | 
				
			||||||
 | 
					def test_warn_on_async_function(testdir):
 | 
				
			||||||
 | 
					    testdir.makepyfile(
 | 
				
			||||||
 | 
					        test_async="""
 | 
				
			||||||
 | 
					        async def test_1():
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					        async def test_2():
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    result = testdir.runpytest()
 | 
				
			||||||
 | 
					    result.stdout.fnmatch_lines(
 | 
				
			||||||
 | 
					        [
 | 
				
			||||||
 | 
					            "test_async.py::test_1",
 | 
				
			||||||
 | 
					            "test_async.py::test_2",
 | 
				
			||||||
 | 
					            "*Coroutine functions are not natively supported*",
 | 
				
			||||||
 | 
					            "*2 skipped, 2 warnings in*",
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    # ensure our warning message appears only once
 | 
				
			||||||
 | 
					    assert (
 | 
				
			||||||
 | 
					        result.stdout.str().count("Coroutine functions are not natively supported") == 1
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue