Skip to content

Commit be4b359

Browse files
authored
Merge pull request #3861 from jonozzz/fix-3854
Fix #3854
2 parents 72a58bb + c336449 commit be4b359

File tree

6 files changed

+123
-6
lines changed

6 files changed

+123
-6
lines changed

changelog/3796.bugfix.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fixed an issue where teardown of fixtures of consecutive sub-packages were executed once, at the end of the outer
2+
package.

changelog/3854.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixes double collection of tests within packages when the filename starts with a capital letter.

src/_pytest/fixtures.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import functools
44
import inspect
5-
import os
65
import sys
76
import warnings
87
from collections import OrderedDict, deque, defaultdict
@@ -93,7 +92,7 @@ def get_scope_package(node, fixturedef):
9392

9493
cls = pytest.Package
9594
current = node
96-
fixture_package_name = os.path.join(fixturedef.baseid, "__init__.py")
95+
fixture_package_name = "%s/%s" % (fixturedef.baseid, "__init__.py")
9796
while current and (
9897
type(current) is not cls or fixture_package_name != current.nodeid
9998
):

src/_pytest/python.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -590,17 +590,28 @@ def collect(self):
590590
self.session.config.pluginmanager._duplicatepaths.remove(path)
591591

592592
this_path = self.fspath.dirpath()
593-
pkg_prefix = None
593+
pkg_prefixes = set()
594594
for path in this_path.visit(rec=self._recurse, bf=True, sort=True):
595595
# we will visit our own __init__.py file, in which case we skip it
596+
skip = False
596597
if path.basename == "__init__.py" and path.dirpath() == this_path:
597598
continue
598-
if pkg_prefix and pkg_prefix in path.parts():
599+
600+
for pkg_prefix in pkg_prefixes:
601+
if (
602+
pkg_prefix in path.parts()
603+
and pkg_prefix.join("__init__.py") != path
604+
):
605+
skip = True
606+
607+
if skip:
599608
continue
609+
610+
if path.isdir() and path.join("__init__.py").check(file=1):
611+
pkg_prefixes.add(path)
612+
600613
for x in self._collectfile(path):
601614
yield x
602-
if isinstance(x, Package):
603-
pkg_prefix = path.dirpath()
604615

605616

606617
def _get_xunit_setup_teardown(holder, attr_name, param_obj=None):

testing/python/collect.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1624,3 +1624,38 @@ def test_package_with_modules(testdir):
16241624
root.chdir()
16251625
result = testdir.runpytest("-v", "-s")
16261626
result.assert_outcomes(passed=2)
1627+
1628+
1629+
def test_package_ordering(testdir):
1630+
"""
1631+
.
1632+
└── root
1633+
├── Test_root.py
1634+
├── __init__.py
1635+
├── sub1
1636+
│ ├── Test_sub1.py
1637+
│ └── __init__.py
1638+
└── sub2
1639+
└── test
1640+
└── test_sub2.py
1641+
1642+
"""
1643+
testdir.makeini(
1644+
"""
1645+
[pytest]
1646+
python_files=*.py
1647+
"""
1648+
)
1649+
root = testdir.mkpydir("root")
1650+
sub1 = root.mkdir("sub1")
1651+
sub1.ensure("__init__.py")
1652+
sub2 = root.mkdir("sub2")
1653+
sub2_test = sub2.mkdir("sub2")
1654+
1655+
root.join("Test_root.py").write("def test_1(): pass")
1656+
sub1.join("Test_sub1.py").write("def test_2(): pass")
1657+
sub2_test.join("test_sub2.py").write("def test_3(): pass")
1658+
1659+
# Execute from .
1660+
result = testdir.runpytest("-v", "-s")
1661+
result.assert_outcomes(passed=3)

testing/python/fixture.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# -*- coding: utf-8 -*-
12
import textwrap
23

34
import pytest
@@ -3977,3 +3978,71 @@ def test_func(self, f2, f1, m2):
39773978
items, _ = testdir.inline_genitems()
39783979
request = FixtureRequest(items[0])
39793980
assert request.fixturenames == "s1 p1 m1 m2 c1 f2 f1".split()
3981+
3982+
def test_multiple_packages(self, testdir):
3983+
"""Complex test involving multiple package fixtures. Make sure teardowns
3984+
are executed in order.
3985+
.
3986+
└── root
3987+
├── __init__.py
3988+
├── sub1
3989+
│ ├── __init__.py
3990+
│ ├── conftest.py
3991+
│ └── test_1.py
3992+
└── sub2
3993+
├── __init__.py
3994+
├── conftest.py
3995+
└── test_2.py
3996+
"""
3997+
root = testdir.mkdir("root")
3998+
root.join("__init__.py").write("values = []")
3999+
sub1 = root.mkdir("sub1")
4000+
sub1.ensure("__init__.py")
4001+
sub1.join("conftest.py").write(
4002+
textwrap.dedent(
4003+
"""\
4004+
import pytest
4005+
from .. import values
4006+
@pytest.fixture(scope="package")
4007+
def fix():
4008+
values.append("pre-sub1")
4009+
yield values
4010+
assert values.pop() == "pre-sub1"
4011+
"""
4012+
)
4013+
)
4014+
sub1.join("test_1.py").write(
4015+
textwrap.dedent(
4016+
"""\
4017+
from .. import values
4018+
def test_1(fix):
4019+
assert values == ["pre-sub1"]
4020+
"""
4021+
)
4022+
)
4023+
sub2 = root.mkdir("sub2")
4024+
sub2.ensure("__init__.py")
4025+
sub2.join("conftest.py").write(
4026+
textwrap.dedent(
4027+
"""\
4028+
import pytest
4029+
from .. import values
4030+
@pytest.fixture(scope="package")
4031+
def fix():
4032+
values.append("pre-sub2")
4033+
yield values
4034+
assert values.pop() == "pre-sub2"
4035+
"""
4036+
)
4037+
)
4038+
sub2.join("test_2.py").write(
4039+
textwrap.dedent(
4040+
"""\
4041+
from .. import values
4042+
def test_2(fix):
4043+
assert values == ["pre-sub2"]
4044+
"""
4045+
)
4046+
)
4047+
reprec = testdir.inline_run()
4048+
reprec.assertoutcome(passed=2)

0 commit comments

Comments
 (0)