Skip to content

Commit 0184cba

Browse files
bcmillsseifertm
authored andcommitted
Refactor tests to use Pytester
Also add type annotations for _create_task_in_context.
1 parent 97c682f commit 0184cba

File tree

2 files changed

+225
-46
lines changed

2 files changed

+225
-46
lines changed

pytest_asyncio/plugin.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from collections.abc import (
1515
AsyncIterator,
1616
Awaitable,
17+
Coroutine as AbstractCoroutine,
1718
Generator,
1819
Iterable,
1920
Iterator,
@@ -410,7 +411,11 @@ def _get_event_loop_fixture_id_for_async_fixture(
410411
return event_loop_fixture_id
411412

412413

413-
def _create_task_in_context(loop, coro, context):
414+
def _create_task_in_context(
415+
loop: asyncio.AbstractEventLoop,
416+
coro: AbstractCoroutine[Any, Any, _T],
417+
context: contextvars.Context,
418+
) -> asyncio.Task[_T]:
414419
"""
415420
Return an asyncio task that runs the coro in the specified context,
416421
if possible.

tests/async_fixtures/test_async_fixtures_contextvars.py

+219-45
Original file line numberDiff line numberDiff line change
@@ -6,68 +6,242 @@
66
from __future__ import annotations
77

88
import sys
9-
from contextlib import contextmanager
10-
from contextvars import ContextVar
9+
from textwrap import dedent
1110

1211
import pytest
13-
14-
_context_var = ContextVar("context_var")
12+
from pytest import Pytester
13+
14+
_prelude = dedent(
15+
"""
16+
import pytest
17+
import pytest_asyncio
18+
from contextlib import contextmanager
19+
from contextvars import ContextVar
20+
21+
_context_var = ContextVar("context_var")
22+
23+
@contextmanager
24+
def context_var_manager(value):
25+
token = _context_var.set(value)
26+
try:
27+
yield
28+
finally:
29+
_context_var.reset(token)
30+
"""
31+
)
1532

1633

17-
@contextmanager
18-
def context_var_manager(value):
19-
token = _context_var.set(value)
20-
try:
21-
yield
22-
finally:
23-
_context_var.reset(token)
34+
def test_var_from_sync_generator_propagates_to_async(pytester: Pytester):
35+
pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function")
36+
pytester.makepyfile(
37+
_prelude
38+
+ dedent(
39+
"""
40+
@pytest.fixture
41+
def var_fixture():
42+
with context_var_manager("value"):
43+
yield
2444
45+
@pytest_asyncio.fixture
46+
async def check_var_fixture(var_fixture):
47+
assert _context_var.get() == "value"
2548
26-
@pytest.fixture(scope="function")
27-
async def no_var_fixture():
28-
with pytest.raises(LookupError):
29-
_context_var.get()
30-
yield
31-
with pytest.raises(LookupError):
32-
_context_var.get()
49+
@pytest.mark.asyncio
50+
async def test(check_var_fixture):
51+
assert _context_var.get() == "value"
52+
"""
53+
)
54+
)
55+
result = pytester.runpytest("--asyncio-mode=strict")
56+
result.assert_outcomes(passed=1)
3357

3458

35-
@pytest.fixture(scope="function")
36-
async def var_fixture_1(no_var_fixture):
37-
with context_var_manager("value1"):
38-
yield
59+
@pytest.mark.xfail(
60+
sys.version_info < (3, 11),
61+
reason="requires asyncio Task context support",
62+
strict=True,
63+
)
64+
def test_var_from_async_generator_propagates_to_sync(pytester: Pytester):
65+
pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function")
66+
pytester.makepyfile(
67+
_prelude
68+
+ dedent(
69+
"""
70+
@pytest_asyncio.fixture
71+
async def var_fixture():
72+
with context_var_manager("value"):
73+
yield
74+
75+
@pytest.fixture
76+
def check_var_fixture(var_fixture):
77+
assert _context_var.get() == "value"
78+
79+
@pytest.mark.asyncio
80+
async def test(check_var_fixture):
81+
assert _context_var.get() == "value"
82+
"""
83+
)
84+
)
85+
result = pytester.runpytest("--asyncio-mode=strict")
86+
result.assert_outcomes(passed=1)
3987

4088

41-
@pytest.fixture(scope="function")
42-
async def var_nop_fixture(var_fixture_1):
43-
with context_var_manager(_context_var.get()):
44-
yield
89+
@pytest.mark.xfail(
90+
sys.version_info < (3, 11),
91+
reason="requires asyncio Task context support",
92+
strict=True,
93+
)
94+
def test_var_from_async_fixture_propagates_to_sync(pytester: Pytester):
95+
pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function")
96+
pytester.makepyfile(
97+
_prelude
98+
+ dedent(
99+
"""
100+
@pytest_asyncio.fixture
101+
async def var_fixture():
102+
_context_var.set("value")
103+
# Rely on async fixture teardown to reset the context var.
104+
105+
@pytest.fixture
106+
def check_var_fixture(var_fixture):
107+
assert _context_var.get() == "value"
108+
109+
def test(check_var_fixture):
110+
assert _context_var.get() == "value"
111+
"""
112+
)
113+
)
114+
result = pytester.runpytest("--asyncio-mode=strict")
115+
result.assert_outcomes(passed=1)
45116

46117

47-
@pytest.fixture(scope="function")
48-
def var_fixture_2(var_nop_fixture):
49-
assert _context_var.get() == "value1"
50-
with context_var_manager("value2"):
51-
yield
118+
@pytest.mark.xfail(
119+
sys.version_info < (3, 11),
120+
reason="requires asyncio Task context support",
121+
strict=True,
122+
)
123+
def test_var_from_generator_reset_before_previous_fixture_cleanup(pytester: Pytester):
124+
pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function")
125+
pytester.makepyfile(
126+
_prelude
127+
+ dedent(
128+
"""
129+
@pytest_asyncio.fixture
130+
async def no_var_fixture():
131+
with pytest.raises(LookupError):
132+
_context_var.get()
133+
yield
134+
with pytest.raises(LookupError):
135+
_context_var.get()
136+
137+
@pytest_asyncio.fixture
138+
async def var_fixture(no_var_fixture):
139+
with context_var_manager("value"):
140+
yield
141+
142+
@pytest.mark.asyncio
143+
async def test(var_fixture):
144+
assert _context_var.get() == "value"
145+
"""
146+
)
147+
)
148+
result = pytester.runpytest("--asyncio-mode=strict")
149+
result.assert_outcomes(passed=1)
52150

53151

54-
@pytest.fixture(scope="function")
55-
async def var_fixture_3(var_fixture_2):
56-
assert _context_var.get() == "value2"
57-
with context_var_manager("value3"):
58-
yield
152+
@pytest.mark.xfail(
153+
sys.version_info < (3, 11),
154+
reason="requires asyncio Task context support",
155+
strict=True,
156+
)
157+
def test_var_from_fixture_reset_before_previous_fixture_cleanup(pytester: Pytester):
158+
pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function")
159+
pytester.makepyfile(
160+
_prelude
161+
+ dedent(
162+
"""
163+
@pytest_asyncio.fixture
164+
async def no_var_fixture():
165+
with pytest.raises(LookupError):
166+
_context_var.get()
167+
yield
168+
with pytest.raises(LookupError):
169+
_context_var.get()
170+
171+
@pytest_asyncio.fixture
172+
async def var_fixture(no_var_fixture):
173+
_context_var.set("value")
174+
# Rely on async fixture teardown to reset the context var.
175+
176+
@pytest.mark.asyncio
177+
async def test(var_fixture):
178+
assert _context_var.get() == "value"
179+
"""
180+
)
181+
)
182+
result = pytester.runpytest("--asyncio-mode=strict")
183+
result.assert_outcomes(passed=1)
59184

60185

61-
@pytest.fixture(scope="function")
62-
async def var_fixture_4(var_fixture_3, request):
63-
assert _context_var.get() == "value3"
64-
_context_var.set("value4")
65-
# Rely on fixture teardown to reset the context var.
186+
@pytest.mark.xfail(
187+
sys.version_info < (3, 11),
188+
reason="requires asyncio Task context support",
189+
strict=True,
190+
)
191+
def test_var_previous_value_restored_after_fixture(pytester: Pytester):
192+
pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function")
193+
pytester.makepyfile(
194+
_prelude
195+
+ dedent(
196+
"""
197+
@pytest_asyncio.fixture
198+
async def var_fixture_1():
199+
with context_var_manager("value1"):
200+
yield
201+
assert _context_var.get() == "value1"
202+
203+
@pytest_asyncio.fixture
204+
async def var_fixture_2(var_fixture_1):
205+
with context_var_manager("value2"):
206+
yield
207+
assert _context_var.get() == "value2"
208+
209+
@pytest.mark.asyncio
210+
async def test(var_fixture_2):
211+
assert _context_var.get() == "value2"
212+
"""
213+
)
214+
)
215+
result = pytester.runpytest("--asyncio-mode=strict")
216+
result.assert_outcomes(passed=1)
66217

67218

68-
@pytest.mark.asyncio
69219
@pytest.mark.xfail(
70-
sys.version_info < (3, 11), reason="requires asyncio Task context support"
220+
sys.version_info < (3, 11),
221+
reason="requires asyncio Task context support",
222+
strict=True,
71223
)
72-
async def test(var_fixture_4):
73-
assert _context_var.get() == "value4"
224+
def test_var_set_to_existing_value_ok(pytester: Pytester):
225+
pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function")
226+
pytester.makepyfile(
227+
_prelude
228+
+ dedent(
229+
"""
230+
@pytest_asyncio.fixture
231+
async def var_fixture():
232+
with context_var_manager("value"):
233+
yield
234+
235+
@pytest_asyncio.fixture
236+
async def same_var_fixture(var_fixture):
237+
with context_var_manager(_context_var.get()):
238+
yield
239+
240+
@pytest.mark.asyncio
241+
async def test(same_var_fixture):
242+
assert _context_var.get() == "value"
243+
"""
244+
)
245+
)
246+
result = pytester.runpytest("--asyncio-mode=strict")
247+
result.assert_outcomes(passed=1)

0 commit comments

Comments
 (0)