Skip to content

Commit 5d3a3ac

Browse files
authored
Merge pull request #12346 from polkapolka/async-tests-issue-warning
make async tests and tests that return non-None fail instead of warn
2 parents ded1f44 + 29490af commit 5d3a3ac

File tree

9 files changed

+35
-70
lines changed

9 files changed

+35
-70
lines changed

Diff for: changelog/11372.improvement.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Async tests will now fail, instead of warning+skipping, if you don't have any suitable plugin installed.

Diff for: changelog/12346.misc.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Tests will now fail, instead of raising a warning, if they return any value other than None.

Diff for: doc/en/changelog.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -1462,7 +1462,7 @@ pytest 7.2.0 (2022-10-23)
14621462
Deprecations
14631463
------------
14641464

1465-
- `#10012 <https://github.com/pytest-dev/pytest/issues/10012>`_: Update :class:`pytest.PytestUnhandledCoroutineWarning` to a deprecation; it will raise an error in pytest 8.
1465+
- `#10012 <https://github.com/pytest-dev/pytest/issues/10012>`_: Update ``pytest.PytestUnhandledCoroutineWarning`` to a deprecation; it will raise an error in pytest 8.
14661466

14671467

14681468
- `#10396 <https://github.com/pytest-dev/pytest/issues/10396>`_: pytest no longer depends on the ``py`` library. ``pytest`` provides a vendored copy of ``py.error`` and ``py.path`` modules but will use the ``py`` library if it is installed. If you need other ``py.*`` modules, continue to install the deprecated ``py`` library separately, otherwise it can usually be removed as a dependency.

Diff for: doc/en/deprecations.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ Returning non-None value in test functions
251251

252252
.. deprecated:: 7.2
253253

254-
A :class:`pytest.PytestReturnNotNoneWarning` is now emitted if a test function returns something other than `None`.
254+
A ``pytest.PytestReturnNotNoneWarning`` is now emitted if a test function returns something other than `None`.
255255

256256
This prevents a common mistake among beginners that expect that returning a `bool` would cause a test to pass or fail, for example:
257257

Diff for: doc/en/reference/reference.rst

-6
Original file line numberDiff line numberDiff line change
@@ -1229,15 +1229,9 @@ Custom warnings generated in some situations such as improper usage or deprecate
12291229
.. autoclass:: pytest.PytestExperimentalApiWarning
12301230
:show-inheritance:
12311231

1232-
.. autoclass:: pytest.PytestReturnNotNoneWarning
1233-
:show-inheritance:
1234-
12351232
.. autoclass:: pytest.PytestRemovedIn9Warning
12361233
:show-inheritance:
12371234

1238-
.. autoclass:: pytest.PytestUnhandledCoroutineWarning
1239-
:show-inheritance:
1240-
12411235
.. autoclass:: pytest.PytestUnknownMarkWarning
12421236
:show-inheritance:
12431237

Diff for: src/_pytest/python.py

+17-19
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,6 @@
7373
from _pytest.scope import Scope
7474
from _pytest.stash import StashKey
7575
from _pytest.warning_types import PytestCollectionWarning
76-
from _pytest.warning_types import PytestReturnNotNoneWarning
77-
from _pytest.warning_types import PytestUnhandledCoroutineWarning
7876

7977

8078
if TYPE_CHECKING:
@@ -135,36 +133,36 @@ def pytest_configure(config: Config) -> None:
135133
)
136134

137135

138-
def async_warn_and_skip(nodeid: str) -> None:
139-
msg = "async def functions are not natively supported and have been skipped.\n"
140-
msg += (
136+
def async_fail(nodeid: str) -> None:
137+
msg = (
138+
"async def functions are not natively supported.\n"
141139
"You need to install a suitable plugin for your async framework, for example:\n"
140+
" - anyio\n"
141+
" - pytest-asyncio\n"
142+
" - pytest-tornasync\n"
143+
" - pytest-trio\n"
144+
" - pytest-twisted"
142145
)
143-
msg += " - anyio\n"
144-
msg += " - pytest-asyncio\n"
145-
msg += " - pytest-tornasync\n"
146-
msg += " - pytest-trio\n"
147-
msg += " - pytest-twisted"
148-
warnings.warn(PytestUnhandledCoroutineWarning(msg.format(nodeid)))
149-
skip(reason="async def function and no async plugin installed (see warnings)")
146+
fail(msg, pytrace=False)
150147

151148

152149
@hookimpl(trylast=True)
153150
def pytest_pyfunc_call(pyfuncitem: Function) -> object | None:
154151
testfunction = pyfuncitem.obj
155152
if is_async_function(testfunction):
156-
async_warn_and_skip(pyfuncitem.nodeid)
153+
async_fail(pyfuncitem.nodeid)
157154
funcargs = pyfuncitem.funcargs
158155
testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}
159156
result = testfunction(**testargs)
160157
if hasattr(result, "__await__") or hasattr(result, "__aiter__"):
161-
async_warn_and_skip(pyfuncitem.nodeid)
158+
async_fail(pyfuncitem.nodeid)
162159
elif result is not None:
163-
warnings.warn(
164-
PytestReturnNotNoneWarning(
165-
f"Expected None, but {pyfuncitem.nodeid} returned {result!r}, which will be an error in a "
166-
"future version of pytest. Did you mean to use `assert` instead of `return`?"
167-
)
160+
fail(
161+
(
162+
f"Expected None, but test returned {result!r}. "
163+
"Did you mean to use `assert` instead of `return`?"
164+
),
165+
pytrace=False,
168166
)
169167
return True
170168

Diff for: src/_pytest/warning_types.py

-18
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,6 @@ class PytestRemovedIn9Warning(PytestDeprecationWarning):
5656
__module__ = "pytest"
5757

5858

59-
class PytestReturnNotNoneWarning(PytestWarning):
60-
"""Warning emitted when a test function is returning value other than None."""
61-
62-
__module__ = "pytest"
63-
64-
6559
@final
6660
class PytestExperimentalApiWarning(PytestWarning, FutureWarning):
6761
"""Warning category used to denote experiments in pytest.
@@ -77,18 +71,6 @@ def simple(cls, apiname: str) -> PytestExperimentalApiWarning:
7771
return cls(f"{apiname} is an experimental api that may change over time")
7872

7973

80-
@final
81-
class PytestUnhandledCoroutineWarning(PytestReturnNotNoneWarning):
82-
"""Warning emitted for an unhandled coroutine.
83-
84-
A coroutine was encountered when collecting test functions, but was not
85-
handled by any async-aware plugin.
86-
Coroutine test functions are not natively supported.
87-
"""
88-
89-
__module__ = "pytest"
90-
91-
9274
@final
9375
class PytestUnknownMarkWarning(PytestWarning):
9476
"""Warning emitted on use of unknown markers.

Diff for: src/pytest/__init__.py

-4
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,6 @@
7878
from _pytest.warning_types import PytestDeprecationWarning
7979
from _pytest.warning_types import PytestExperimentalApiWarning
8080
from _pytest.warning_types import PytestRemovedIn9Warning
81-
from _pytest.warning_types import PytestReturnNotNoneWarning
82-
from _pytest.warning_types import PytestUnhandledCoroutineWarning
8381
from _pytest.warning_types import PytestUnhandledThreadExceptionWarning
8482
from _pytest.warning_types import PytestUnknownMarkWarning
8583
from _pytest.warning_types import PytestUnraisableExceptionWarning
@@ -142,10 +140,8 @@
142140
"PytestDeprecationWarning",
143141
"PytestExperimentalApiWarning",
144142
"PytestRemovedIn9Warning",
145-
"PytestReturnNotNoneWarning",
146143
"Pytester",
147144
"PytestPluginManager",
148-
"PytestUnhandledCoroutineWarning",
149145
"PytestUnhandledThreadExceptionWarning",
150146
"PytestUnknownMarkWarning",
151147
"PytestUnraisableExceptionWarning",

Diff for: testing/acceptance_test.py

+14-21
Original file line numberDiff line numberDiff line change
@@ -1235,7 +1235,7 @@ def test_usage_error_code(pytester: Pytester) -> None:
12351235
assert result.ret == ExitCode.USAGE_ERROR
12361236

12371237

1238-
def test_warn_on_async_function(pytester: Pytester) -> None:
1238+
def test_error_on_async_function(pytester: Pytester) -> None:
12391239
# In the below we .close() the coroutine only to avoid
12401240
# "RuntimeWarning: coroutine 'test_2' was never awaited"
12411241
# which messes with other tests.
@@ -1251,23 +1251,19 @@ def test_3():
12511251
return coro
12521252
"""
12531253
)
1254-
result = pytester.runpytest("-Wdefault")
1254+
result = pytester.runpytest()
12551255
result.stdout.fnmatch_lines(
12561256
[
1257-
"test_async.py::test_1",
1258-
"test_async.py::test_2",
1259-
"test_async.py::test_3",
12601257
"*async def functions are not natively supported*",
1261-
"*3 skipped, 3 warnings in*",
1258+
"*test_async.py::test_1*",
1259+
"*test_async.py::test_2*",
1260+
"*test_async.py::test_3*",
12621261
]
12631262
)
1264-
# ensure our warning message appears only once
1265-
assert (
1266-
result.stdout.str().count("async def functions are not natively supported") == 1
1267-
)
1263+
result.assert_outcomes(failed=3)
12681264

12691265

1270-
def test_warn_on_async_gen_function(pytester: Pytester) -> None:
1266+
def test_error_on_async_gen_function(pytester: Pytester) -> None:
12711267
pytester.makepyfile(
12721268
test_async="""
12731269
async def test_1():
@@ -1278,20 +1274,16 @@ def test_3():
12781274
return test_2()
12791275
"""
12801276
)
1281-
result = pytester.runpytest("-Wdefault")
1277+
result = pytester.runpytest()
12821278
result.stdout.fnmatch_lines(
12831279
[
1284-
"test_async.py::test_1",
1285-
"test_async.py::test_2",
1286-
"test_async.py::test_3",
12871280
"*async def functions are not natively supported*",
1288-
"*3 skipped, 3 warnings in*",
1281+
"*test_async.py::test_1*",
1282+
"*test_async.py::test_2*",
1283+
"*test_async.py::test_3*",
12891284
]
12901285
)
1291-
# ensure our warning message appears only once
1292-
assert (
1293-
result.stdout.str().count("async def functions are not natively supported") == 1
1294-
)
1286+
result.assert_outcomes(failed=3)
12951287

12961288

12971289
def test_pdb_can_be_rewritten(pytester: Pytester) -> None:
@@ -1377,14 +1369,15 @@ def test_no_brokenpipeerror_message(pytester: Pytester) -> None:
13771369
popen.stderr.close()
13781370

13791371

1380-
def test_function_return_non_none_warning(pytester: Pytester) -> None:
1372+
def test_function_return_non_none_error(pytester: Pytester) -> None:
13811373
pytester.makepyfile(
13821374
"""
13831375
def test_stuff():
13841376
return "something"
13851377
"""
13861378
)
13871379
res = pytester.runpytest()
1380+
res.assert_outcomes(failed=1)
13881381
res.stdout.fnmatch_lines(["*Did you mean to use `assert` instead of `return`?*"])
13891382

13901383

0 commit comments

Comments
 (0)