Skip to content

Commit 44a3ab3

Browse files
committed
If a fixture cancels the test, that's always a failure
Even if it doesn't raise an exception afterwards.
1 parent 0a02966 commit 44a3ab3

File tree

2 files changed

+51
-4
lines changed

2 files changed

+51
-4
lines changed

pytest_trio/_tests/test_fixture_mistakes.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,32 @@ def test_whatever(async_fixture):
117117
result.stdout.fnmatch_lines(
118118
["*: Trio fixtures can only be used by Trio tests*"]
119119
)
120+
121+
122+
@enable_trio_mode
123+
def test_fixture_cancels_test_but_doesnt_raise(testdir, enable_trio_mode):
124+
enable_trio_mode(testdir)
125+
126+
testdir.makepyfile(
127+
"""
128+
import pytest
129+
import trio
130+
131+
@pytest.fixture
132+
async def async_fixture():
133+
with trio.CancelScope() as cscope:
134+
cscope.cancel()
135+
yield
136+
137+
138+
async def test_whatever(async_fixture):
139+
pass
140+
"""
141+
)
142+
143+
result = testdir.runpytest()
144+
145+
result.assert_outcomes(failed=1)
146+
result.stdout.fnmatch_lines(
147+
["*async_fixture*cancelled the test*"]
148+
)

pytest_trio/plugin.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,16 @@ class TrioTestContext:
133133
def __init__(self):
134134
self.crashed = False
135135
self.test_cancel_scope = None
136+
self.fixtures_with_errors = set()
137+
self.fixtures_with_cancel = set()
136138
self.error_list = []
137139

138-
def crash(self, exc):
139-
if exc is not None:
140+
def crash(self, fixture, exc):
141+
if exc is None:
142+
self.fixtures_with_cancel.add(fixture)
143+
else:
140144
self.error_list.append(exc)
145+
self.fixtures_with_errors.add(fixture)
141146
self.crashed = True
142147
if self.test_cancel_scope is not None:
143148
self.test_cancel_scope.cancel()
@@ -193,7 +198,7 @@ async def _fixture_manager(self, test_ctx):
193198
finally:
194199
nursery_fixture.cancel_scope.cancel()
195200
except BaseException as exc:
196-
test_ctx.crash(exc)
201+
test_ctx.crash(self, exc)
197202
finally:
198203
self.setup_done.set()
199204
self._teardown_done.set()
@@ -286,7 +291,7 @@ async def run(self, test_ctx, contextvars_ctx):
286291
except BaseException as exc:
287292
assert isinstance(exc, trio.Cancelled)
288293
yield_outcome = outcome.Error(exc)
289-
test_ctx.crash(None)
294+
test_ctx.crash(self, None)
290295
with trio.CancelScope(shield=True):
291296
for event in self.user_done_events:
292297
await event.wait()
@@ -342,6 +347,19 @@ async def _bootstrap_fixtures_and_run_test(**kwargs):
342347
fixture.run, test_ctx, contextvars_ctx, name=fixture.name
343348
)
344349

350+
silent_cancellers = (
351+
test_ctx.fixtures_with_cancel - test_ctx.fixtures_with_errors
352+
)
353+
if silent_cancellers:
354+
for fixture in silent_cancellers:
355+
test_ctx.error_list.append(
356+
RuntimeError(
357+
"{} cancelled the test but didn't "
358+
"raise an error"
359+
.format(fixture.name)
360+
)
361+
)
362+
345363
if test_ctx.error_list:
346364
raise trio.MultiError(test_ctx.error_list)
347365

0 commit comments

Comments
 (0)