File tree Expand file tree Collapse file tree 2 files changed +78
-0
lines changed Expand file tree Collapse file tree 2 files changed +78
-0
lines changed Original file line number Diff line number Diff line change 2626)
2727
2828import pytest
29+ from _pytest .mark .structures import get_unpacked_marks
2930from pytest import (
3031 Config ,
3132 FixtureRequest ,
@@ -339,6 +340,33 @@ def pytest_pycollect_makeitem(
339340 return None
340341
341342
343+ @pytest .hookimpl
344+ def pytest_collectstart (collector : pytest .Collector ):
345+ if not isinstance (collector , pytest .Class ):
346+ return
347+ # pytest.Collector.own_markers is empty at this point,
348+ # so we rely on _pytest.mark.structures.get_unpacked_marks
349+ marks = get_unpacked_marks (collector .obj , consider_mro = True )
350+ for mark in marks :
351+ if not mark .name == "asyncio" :
352+ continue
353+
354+ @pytest .fixture (
355+ scope = "class" ,
356+ name = "event_loop" ,
357+ )
358+ def scoped_event_loop (cls ) -> Iterator [asyncio .AbstractEventLoop ]:
359+ loop = asyncio .get_event_loop_policy ().new_event_loop ()
360+ yield loop
361+ loop .close ()
362+
363+ # @pytest.fixture does not register the fixture anywhere, so pytest doesn't
364+ # know it exists. We work around this by attaching the fixture function to the
365+ # collected Python class, where it will be picked up by pytest.Class.collect()
366+ collector .obj .__pytest_asyncio_scoped_event_loop = scoped_event_loop
367+ break
368+
369+
342370def pytest_collection_modifyitems (
343371 session : Session , config : Config , items : List [Item ]
344372) -> None :
Original file line number Diff line number Diff line change 11"""Test if pytestmark works when defined on a class."""
22import asyncio
3+ from textwrap import dedent
34
45import pytest
56
@@ -23,3 +24,52 @@ async def inc():
2324@pytest .fixture
2425def sample_fixture ():
2526 return None
27+
28+
29+ def test_asyncio_mark_provides_class_scoped_loop (pytester : pytest .Pytester ):
30+ pytester .makepyfile (
31+ dedent (
32+ """\
33+ import asyncio
34+ import pytest
35+
36+ @pytest.mark.asyncio
37+ class TestClassScopedLoop:
38+ loop: asyncio.AbstractEventLoop
39+
40+ async def test_remember_loop(self):
41+ TestClassScopedLoop.loop = asyncio.get_running_loop()
42+
43+ async def test_this_runs_in_same_loop(self):
44+ assert asyncio.get_running_loop() is TestClassScopedLoop.loop
45+ """
46+ )
47+ )
48+ result = pytester .runpytest ("--asyncio-mode=strict" )
49+ result .assert_outcomes (passed = 2 )
50+
51+
52+ def test_asyncio_mark_is_inherited_to_subclasses (pytester : pytest .Pytester ):
53+ pytester .makepyfile (
54+ dedent (
55+ """\
56+ import asyncio
57+ import pytest
58+
59+ @pytest.mark.asyncio
60+ class TestSuperClassWithMark:
61+ pass
62+
63+ class TestWithoutMark(TestSuperClassWithMark):
64+ loop: asyncio.AbstractEventLoop
65+
66+ async def test_remember_loop(self):
67+ TestWithoutMark.loop = asyncio.get_running_loop()
68+
69+ async def test_this_runs_in_same_loop(self):
70+ assert asyncio.get_running_loop() is TestWithoutMark.loop
71+ """
72+ )
73+ )
74+ result = pytester .runpytest ("--asyncio-mode=strict" )
75+ result .assert_outcomes (passed = 2 )
You can’t perform that action at this time.
0 commit comments