diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 935be84c1..43ccc97c6 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -50,7 +50,6 @@ from _pytest.monkeypatch import MonkeyPatch from _pytest.nodes import Collector from _pytest.nodes import Item from _pytest.pathlib import make_numbered_dir -from _pytest.python import Module from _pytest.reports import CollectReport from _pytest.reports import TestReport from _pytest.tmpdir import TempPathFactory @@ -652,7 +651,7 @@ class Pytester: ) -> None: self._request = request self._mod_collections: WeakKeyDictionary[ - Module, List[Union[Item, Collector]] + Collector, List[Union[Item, Collector]] ] = (WeakKeyDictionary()) if request.function: name: str = request.function.__name__ @@ -1244,7 +1243,7 @@ class Pytester: return self.getnode(config, path) def collect_by_name( - self, modcol: Module, name: str + self, modcol: Collector, name: str ) -> Optional[Union[Item, Collector]]: """Return the collection node for name from the module collection. @@ -1639,7 +1638,7 @@ class Testdir: ) def collect_by_name( - self, modcol: Module, name: str + self, modcol: Collector, name: str ) -> Optional[Union[Item, Collector]]: """See :meth:`Pytester.collect_by_name`.""" return self._pytester.collect_by_name(modcol, name) diff --git a/testing/test_collection.py b/testing/test_collection.py index 2fa74605f..1138c2bd6 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -1,42 +1,56 @@ import os import pprint +import shutil import sys import textwrap from pathlib import Path +from typing import List import pytest from _pytest.config import ExitCode +from _pytest.fixtures import FixtureRequest from _pytest.main import _in_venv from _pytest.main import Session +from _pytest.monkeypatch import MonkeyPatch +from _pytest.nodes import Item from _pytest.pathlib import symlink_or_skip +from _pytest.pytester import HookRecorder from _pytest.pytester import Pytester from _pytest.pytester import Testdir +def ensure_file(file_path: Path) -> Path: + """Ensure that file exists""" + file_path.parent.mkdir(parents=True, exist_ok=True) + file_path.touch(exist_ok=True) + return file_path + + class TestCollector: - def test_collect_versus_item(self): - from pytest import Collector, Item + def test_collect_versus_item(self) -> None: + from pytest import Collector + from pytest import Item assert not issubclass(Collector, Item) assert not issubclass(Item, Collector) - def test_check_equality(self, testdir: Testdir) -> None: - modcol = testdir.getmodulecol( + def test_check_equality(self, pytester: Pytester) -> None: + modcol = pytester.getmodulecol( """ def test_pass(): pass def test_fail(): assert 0 """ ) - fn1 = testdir.collect_by_name(modcol, "test_pass") + fn1 = pytester.collect_by_name(modcol, "test_pass") assert isinstance(fn1, pytest.Function) - fn2 = testdir.collect_by_name(modcol, "test_pass") + fn2 = pytester.collect_by_name(modcol, "test_pass") assert isinstance(fn2, pytest.Function) assert fn1 == fn2 assert fn1 != modcol assert hash(fn1) == hash(fn2) - fn3 = testdir.collect_by_name(modcol, "test_fail") + fn3 = pytester.collect_by_name(modcol, "test_fail") assert isinstance(fn3, pytest.Function) assert not (fn1 == fn3) assert fn1 != fn3 @@ -49,31 +63,35 @@ class TestCollector: assert [1, 2, 3] != fn # type: ignore[comparison-overlap] assert modcol != fn - assert testdir.collect_by_name(modcol, "doesnotexist") is None + assert pytester.collect_by_name(modcol, "doesnotexist") is None - def test_getparent(self, testdir): - modcol = testdir.getmodulecol( + def test_getparent(self, pytester: Pytester) -> None: + modcol = pytester.getmodulecol( """ class TestClass: def test_foo(self): pass """ ) - cls = testdir.collect_by_name(modcol, "TestClass") - fn = testdir.collect_by_name(testdir.collect_by_name(cls, "()"), "test_foo") + cls = pytester.collect_by_name(modcol, "TestClass") + assert isinstance(cls, pytest.Class) + instance = pytester.collect_by_name(cls, "()") + assert isinstance(instance, pytest.Instance) + fn = pytester.collect_by_name(instance, "test_foo") + assert isinstance(fn, pytest.Function) - parent = fn.getparent(pytest.Module) - assert parent is modcol + module_parent = fn.getparent(pytest.Module) + assert module_parent is modcol - parent = fn.getparent(pytest.Function) - assert parent is fn + function_parent = fn.getparent(pytest.Function) + assert function_parent is fn - parent = fn.getparent(pytest.Class) - assert parent is cls + class_parent = fn.getparent(pytest.Class) + assert class_parent is cls - def test_getcustomfile_roundtrip(self, testdir): - hello = testdir.makefile(".xxx", hello="world") - testdir.makepyfile( + def test_getcustomfile_roundtrip(self, pytester: Pytester) -> None: + hello = pytester.makefile(".xxx", hello="world") + pytester.makepyfile( conftest=""" import pytest class CustomFile(pytest.File): @@ -83,16 +101,16 @@ class TestCollector: return CustomFile.from_parent(fspath=path, parent=parent) """ ) - node = testdir.getpathnode(hello) + node = pytester.getpathnode(hello) assert isinstance(node, pytest.File) assert node.name == "hello.xxx" nodes = node.session.perform_collect([node.nodeid], genitems=False) assert len(nodes) == 1 assert isinstance(nodes[0], pytest.File) - def test_can_skip_class_with_test_attr(self, testdir): + def test_can_skip_class_with_test_attr(self, pytester: Pytester) -> None: """Assure test class is skipped when using `__test__=False` (See #2007).""" - testdir.makepyfile( + pytester.makepyfile( """ class TestFoo(object): __test__ = False @@ -102,25 +120,25 @@ class TestCollector: assert True """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["collected 0 items", "*no tests ran in*"]) class TestCollectFS: - def test_ignored_certain_directories(self, testdir): - tmpdir = testdir.tmpdir - tmpdir.ensure("build", "test_notfound.py") - tmpdir.ensure("dist", "test_notfound.py") - tmpdir.ensure("_darcs", "test_notfound.py") - tmpdir.ensure("CVS", "test_notfound.py") - tmpdir.ensure("{arch}", "test_notfound.py") - tmpdir.ensure(".whatever", "test_notfound.py") - tmpdir.ensure(".bzr", "test_notfound.py") - tmpdir.ensure("normal", "test_found.py") + def test_ignored_certain_directories(self, pytester: Pytester) -> None: + tmpdir = pytester.path + ensure_file(tmpdir / "build" / "test_notfound.py") + ensure_file(tmpdir / "dist" / "test_notfound.py") + ensure_file(tmpdir / "_darcs" / "test_notfound.py") + ensure_file(tmpdir / "CVS" / "test_notfound.py") + ensure_file(tmpdir / "{arch}" / "test_notfound.py") + ensure_file(tmpdir / ".whatever" / "test_notfound.py") + ensure_file(tmpdir / ".bzr" / "test_notfound.py") + ensure_file(tmpdir / "normal" / "test_found.py") for x in Path(str(tmpdir)).rglob("test_*.py"): x.write_text("def test_hello(): pass", "utf-8") - result = testdir.runpytest("--collect-only") + result = pytester.runpytest("--collect-only") s = result.stdout.str() assert "test_notfound" not in s assert "test_found" in s @@ -136,20 +154,20 @@ class TestCollectFS: "Activate.ps1", ), ) - def test_ignored_virtualenvs(self, testdir, fname): + def test_ignored_virtualenvs(self, pytester: Pytester, fname: str) -> None: bindir = "Scripts" if sys.platform.startswith("win") else "bin" - testdir.tmpdir.ensure("virtual", bindir, fname) - testfile = testdir.tmpdir.ensure("virtual", "test_invenv.py") - testfile.write("def test_hello(): pass") + ensure_file(pytester.path / "virtual" / bindir / fname) + testfile = ensure_file(pytester.path / "virtual" / "test_invenv.py") + testfile.write_text("def test_hello(): pass") # by default, ignore tests inside a virtualenv - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.no_fnmatch_line("*test_invenv*") # allow test collection if user insists - result = testdir.runpytest("--collect-in-virtualenv") + result = pytester.runpytest("--collect-in-virtualenv") assert "test_invenv" in result.stdout.str() # allow test collection if user directly passes in the directory - result = testdir.runpytest("virtual") + result = pytester.runpytest("virtual") assert "test_invenv" in result.stdout.str() @pytest.mark.parametrize( @@ -163,16 +181,18 @@ class TestCollectFS: "Activate.ps1", ), ) - def test_ignored_virtualenvs_norecursedirs_precedence(self, testdir, fname): + def test_ignored_virtualenvs_norecursedirs_precedence( + self, pytester: Pytester, fname: str + ) -> None: bindir = "Scripts" if sys.platform.startswith("win") else "bin" # norecursedirs takes priority - testdir.tmpdir.ensure(".virtual", bindir, fname) - testfile = testdir.tmpdir.ensure(".virtual", "test_invenv.py") - testfile.write("def test_hello(): pass") - result = testdir.runpytest("--collect-in-virtualenv") + ensure_file(pytester.path / ".virtual" / bindir / fname) + testfile = ensure_file(pytester.path / ".virtual" / "test_invenv.py") + testfile.write_text("def test_hello(): pass") + result = pytester.runpytest("--collect-in-virtualenv") result.stdout.no_fnmatch_line("*test_invenv*") # ...unless the virtualenv is explicitly given on the CLI - result = testdir.runpytest("--collect-in-virtualenv", ".virtual") + result = pytester.runpytest("--collect-in-virtualenv", ".virtual") assert "test_invenv" in result.stdout.str() @pytest.mark.parametrize( @@ -186,7 +206,7 @@ class TestCollectFS: "Activate.ps1", ), ) - def test__in_venv(self, testdir, fname): + def test__in_venv(self, testdir: Testdir, fname: str) -> None: """Directly test the virtual env detection function""" bindir = "Scripts" if sys.platform.startswith("win") else "bin" # no bin/activate, not a virtualenv @@ -196,55 +216,55 @@ class TestCollectFS: base_path.ensure(bindir, fname) assert _in_venv(base_path) is True - def test_custom_norecursedirs(self, testdir): - testdir.makeini( + def test_custom_norecursedirs(self, pytester: Pytester) -> None: + pytester.makeini( """ [pytest] norecursedirs = mydir xyz* """ ) - tmpdir = testdir.tmpdir - tmpdir.ensure("mydir", "test_hello.py").write("def test_1(): pass") - tmpdir.ensure("xyz123", "test_2.py").write("def test_2(): 0/0") - tmpdir.ensure("xy", "test_ok.py").write("def test_3(): pass") - rec = testdir.inline_run() + tmpdir = pytester.path + ensure_file(tmpdir / "mydir" / "test_hello.py").write_text("def test_1(): pass") + ensure_file(tmpdir / "xyz123" / "test_2.py").write_text("def test_2(): 0/0") + ensure_file(tmpdir / "xy" / "test_ok.py").write_text("def test_3(): pass") + rec = pytester.inline_run() rec.assertoutcome(passed=1) - rec = testdir.inline_run("xyz123/test_2.py") + rec = pytester.inline_run("xyz123/test_2.py") rec.assertoutcome(failed=1) - def test_testpaths_ini(self, testdir, monkeypatch): - testdir.makeini( + def test_testpaths_ini(self, pytester: Pytester, monkeypatch: MonkeyPatch) -> None: + pytester.makeini( """ [pytest] testpaths = gui uts """ ) - tmpdir = testdir.tmpdir - tmpdir.ensure("env", "test_1.py").write("def test_env(): pass") - tmpdir.ensure("gui", "test_2.py").write("def test_gui(): pass") - tmpdir.ensure("uts", "test_3.py").write("def test_uts(): pass") + tmpdir = pytester.path + ensure_file(tmpdir / "env" / "test_1.py").write_text("def test_env(): pass") + ensure_file(tmpdir / "gui" / "test_2.py").write_text("def test_gui(): pass") + ensure_file(tmpdir / "uts" / "test_3.py").write_text("def test_uts(): pass") # executing from rootdir only tests from `testpaths` directories # are collected - items, reprec = testdir.inline_genitems("-v") + items, reprec = pytester.inline_genitems("-v") assert [x.name for x in items] == ["test_gui", "test_uts"] # check that explicitly passing directories in the command-line # collects the tests for dirname in ("env", "gui", "uts"): - items, reprec = testdir.inline_genitems(tmpdir.join(dirname)) + items, reprec = pytester.inline_genitems(tmpdir.joinpath(dirname)) assert [x.name for x in items] == ["test_%s" % dirname] # changing cwd to each subdirectory and running pytest without # arguments collects the tests in that directory normally for dirname in ("env", "gui", "uts"): - monkeypatch.chdir(testdir.tmpdir.join(dirname)) - items, reprec = testdir.inline_genitems() + monkeypatch.chdir(pytester.path.joinpath(dirname)) + items, reprec = pytester.inline_genitems() assert [x.name for x in items] == ["test_%s" % dirname] class TestCollectPluginHookRelay: - def test_pytest_collect_file(self, testdir): + def test_pytest_collect_file(self, testdir: Testdir) -> None: wascalled = [] class Plugin: @@ -254,19 +274,19 @@ class TestCollectPluginHookRelay: wascalled.append(path) testdir.makefile(".abc", "xyz") - pytest.main([testdir.tmpdir], plugins=[Plugin()]) + pytest.main(testdir.tmpdir, plugins=[Plugin()]) assert len(wascalled) == 1 assert wascalled[0].ext == ".abc" class TestPrunetraceback: - def test_custom_repr_failure(self, testdir): - p = testdir.makepyfile( + def test_custom_repr_failure(self, pytester: Pytester) -> None: + p = pytester.makepyfile( """ import not_exists """ ) - testdir.makeconftest( + pytester.makeconftest( """ import pytest def pytest_collect_file(path, parent): @@ -283,17 +303,17 @@ class TestPrunetraceback: """ ) - result = testdir.runpytest(p) + result = pytester.runpytest(p) result.stdout.fnmatch_lines(["*ERROR collecting*", "*hello world*"]) @pytest.mark.xfail(reason="other mechanism for adding to reporting needed") - def test_collect_report_postprocessing(self, testdir): - p = testdir.makepyfile( + def test_collect_report_postprocessing(self, pytester: Pytester) -> None: + p = pytester.makepyfile( """ import not_exists """ ) - testdir.makeconftest( + pytester.makeconftest( """ import pytest @pytest.hookimpl(hookwrapper=True) @@ -304,45 +324,45 @@ class TestPrunetraceback: outcome.force_result(rep) """ ) - result = testdir.runpytest(p) + result = pytester.runpytest(p) result.stdout.fnmatch_lines(["*ERROR collecting*", "*header1*"]) class TestCustomConftests: - def test_ignore_collect_path(self, testdir): - testdir.makeconftest( + def test_ignore_collect_path(self, pytester: Pytester) -> None: + pytester.makeconftest( """ def pytest_ignore_collect(path, config): return path.basename.startswith("x") or \ path.basename == "test_one.py" """ ) - sub = testdir.mkdir("xy123") - sub.ensure("test_hello.py").write("syntax error") - sub.join("conftest.py").write("syntax error") - testdir.makepyfile("def test_hello(): pass") - testdir.makepyfile(test_one="syntax error") - result = testdir.runpytest("--fulltrace") + sub = pytester.mkdir("xy123") + ensure_file(sub / "test_hello.py").write_text("syntax error") + sub.joinpath("conftest.py").write_text("syntax error") + pytester.makepyfile("def test_hello(): pass") + pytester.makepyfile(test_one="syntax error") + result = pytester.runpytest("--fulltrace") assert result.ret == 0 result.stdout.fnmatch_lines(["*1 passed*"]) - def test_ignore_collect_not_called_on_argument(self, testdir): - testdir.makeconftest( + def test_ignore_collect_not_called_on_argument(self, pytester: Pytester) -> None: + pytester.makeconftest( """ def pytest_ignore_collect(path, config): return True """ ) - p = testdir.makepyfile("def test_hello(): pass") - result = testdir.runpytest(p) + p = pytester.makepyfile("def test_hello(): pass") + result = pytester.runpytest(p) assert result.ret == 0 result.stdout.fnmatch_lines(["*1 passed*"]) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret == ExitCode.NO_TESTS_COLLECTED result.stdout.fnmatch_lines(["*collected 0 items*"]) - def test_collectignore_exclude_on_option(self, testdir): - testdir.makeconftest( + def test_collectignore_exclude_on_option(self, pytester: Pytester) -> None: + pytester.makeconftest( """ collect_ignore = ['hello', 'test_world.py'] def pytest_addoption(parser): @@ -352,17 +372,17 @@ class TestCustomConftests: collect_ignore[:] = [] """ ) - testdir.mkdir("hello") - testdir.makepyfile(test_world="def test_hello(): pass") - result = testdir.runpytest() + pytester.mkdir("hello") + pytester.makepyfile(test_world="def test_hello(): pass") + result = pytester.runpytest() assert result.ret == ExitCode.NO_TESTS_COLLECTED result.stdout.no_fnmatch_line("*passed*") - result = testdir.runpytest("--XX") + result = pytester.runpytest("--XX") assert result.ret == 0 assert "passed" in result.stdout.str() - def test_collectignoreglob_exclude_on_option(self, testdir): - testdir.makeconftest( + def test_collectignoreglob_exclude_on_option(self, pytester: Pytester) -> None: + pytester.makeconftest( """ collect_ignore_glob = ['*w*l[dt]*'] def pytest_addoption(parser): @@ -372,17 +392,17 @@ class TestCustomConftests: collect_ignore_glob[:] = [] """ ) - testdir.makepyfile(test_world="def test_hello(): pass") - testdir.makepyfile(test_welt="def test_hallo(): pass") - result = testdir.runpytest() + pytester.makepyfile(test_world="def test_hello(): pass") + pytester.makepyfile(test_welt="def test_hallo(): pass") + result = pytester.runpytest() assert result.ret == ExitCode.NO_TESTS_COLLECTED result.stdout.fnmatch_lines(["*collected 0 items*"]) - result = testdir.runpytest("--XX") + result = pytester.runpytest("--XX") assert result.ret == 0 result.stdout.fnmatch_lines(["*2 passed*"]) - def test_pytest_fs_collect_hooks_are_seen(self, testdir): - testdir.makeconftest( + def test_pytest_fs_collect_hooks_are_seen(self, pytester: Pytester) -> None: + pytester.makeconftest( """ import pytest class MyModule(pytest.Module): @@ -392,15 +412,15 @@ class TestCustomConftests: return MyModule.from_parent(fspath=path, parent=parent) """ ) - testdir.mkdir("sub") - testdir.makepyfile("def test_x(): pass") - result = testdir.runpytest("--co") + pytester.mkdir("sub") + pytester.makepyfile("def test_x(): pass") + result = pytester.runpytest("--co") result.stdout.fnmatch_lines(["*MyModule*", "*test_x*"]) - def test_pytest_collect_file_from_sister_dir(self, testdir): - sub1 = testdir.mkpydir("sub1") - sub2 = testdir.mkpydir("sub2") - conf1 = testdir.makeconftest( + def test_pytest_collect_file_from_sister_dir(self, pytester: Pytester) -> None: + sub1 = pytester.mkpydir("sub1") + sub2 = pytester.mkpydir("sub2") + conf1 = pytester.makeconftest( """ import pytest class MyModule1(pytest.Module): @@ -410,8 +430,8 @@ class TestCustomConftests: return MyModule1.from_parent(fspath=path, parent=parent) """ ) - conf1.move(sub1.join(conf1.basename)) - conf2 = testdir.makeconftest( + conf1.replace(sub1.joinpath(conf1.name)) + conf2 = pytester.makeconftest( """ import pytest class MyModule2(pytest.Module): @@ -421,21 +441,21 @@ class TestCustomConftests: return MyModule2.from_parent(fspath=path, parent=parent) """ ) - conf2.move(sub2.join(conf2.basename)) - p = testdir.makepyfile("def test_x(): pass") - p.copy(sub1.join(p.basename)) - p.copy(sub2.join(p.basename)) - result = testdir.runpytest("--co") + conf2.replace(sub2.joinpath(conf2.name)) + p = pytester.makepyfile("def test_x(): pass") + shutil.copy(p, sub1.joinpath(p.name)) + shutil.copy(p, sub2.joinpath(p.name)) + result = pytester.runpytest("--co") result.stdout.fnmatch_lines(["*MyModule1*", "*MyModule2*", "*test_x*"]) class TestSession: - def test_collect_topdir(self, testdir): - p = testdir.makepyfile("def test_func(): pass") - id = "::".join([p.basename, "test_func"]) + def test_collect_topdir(self, pytester: Pytester) -> None: + p = pytester.makepyfile("def test_func(): pass") + id = "::".join([p.name, "test_func"]) # XXX migrate to collectonly? (see below) - config = testdir.parseconfig(id) - topdir = testdir.tmpdir + config = pytester.parseconfig(id) + topdir = pytester.path rcol = Session.from_config(config) assert topdir == rcol.fspath # rootid = rcol.nodeid @@ -445,7 +465,7 @@ class TestSession: assert len(colitems) == 1 assert colitems[0].fspath == p - def get_reported_items(self, hookrec): + def get_reported_items(self, hookrec: HookRecorder) -> List[Item]: """Return pytest.Item instances reported by the pytest_collectreport hook""" calls = hookrec.getcalls("pytest_collectreport") return [ @@ -455,16 +475,16 @@ class TestSession: if isinstance(x, pytest.Item) ] - def test_collect_protocol_single_function(self, testdir): - p = testdir.makepyfile("def test_func(): pass") - id = "::".join([p.basename, "test_func"]) - items, hookrec = testdir.inline_genitems(id) + def test_collect_protocol_single_function(self, pytester: Pytester) -> None: + p = pytester.makepyfile("def test_func(): pass") + id = "::".join([p.name, "test_func"]) + items, hookrec = pytester.inline_genitems(id) (item,) = items assert item.name == "test_func" newid = item.nodeid assert newid == id pprint.pprint(hookrec.calls) - topdir = testdir.tmpdir # noqa + topdir = pytester.path # noqa hookrec.assert_contains( [ ("pytest_collectstart", "collector.fspath == topdir"), @@ -478,17 +498,17 @@ class TestSession: # ensure we are reporting the collection of the single test item (#2464) assert [x.name for x in self.get_reported_items(hookrec)] == ["test_func"] - def test_collect_protocol_method(self, testdir): - p = testdir.makepyfile( + def test_collect_protocol_method(self, pytester: Pytester) -> None: + p = pytester.makepyfile( """ class TestClass(object): def test_method(self): pass """ ) - normid = p.basename + "::TestClass::test_method" - for id in [p.basename, p.basename + "::TestClass", normid]: - items, hookrec = testdir.inline_genitems(id) + normid = p.name + "::TestClass::test_method" + for id in [p.name, p.name + "::TestClass", normid]: + items, hookrec = pytester.inline_genitems(id) assert len(items) == 1 assert items[0].name == "test_method" newid = items[0].nodeid @@ -496,9 +516,9 @@ class TestSession: # ensure we are reporting the collection of the single test item (#2464) assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"] - def test_collect_custom_nodes_multi_id(self, testdir): - p = testdir.makepyfile("def test_func(): pass") - testdir.makeconftest( + def test_collect_custom_nodes_multi_id(self, pytester: Pytester) -> None: + p = pytester.makepyfile("def test_func(): pass") + pytester.makeconftest( """ import pytest class SpecialItem(pytest.Item): @@ -511,11 +531,11 @@ class TestSession: if path.basename == %r: return SpecialFile.from_parent(fspath=path, parent=parent) """ - % p.basename + % p.name ) - id = p.basename + id = p.name - items, hookrec = testdir.inline_genitems(id) + items, hookrec = pytester.inline_genitems(id) pprint.pprint(hookrec.calls) assert len(items) == 2 hookrec.assert_contains( @@ -527,18 +547,18 @@ class TestSession: ), ("pytest_collectstart", "collector.__class__.__name__ == 'Module'"), ("pytest_pycollect_makeitem", "name == 'test_func'"), - ("pytest_collectreport", "report.nodeid.startswith(p.basename)"), + ("pytest_collectreport", "report.nodeid.startswith(p.name)"), ] ) assert len(self.get_reported_items(hookrec)) == 2 - def test_collect_subdir_event_ordering(self, testdir): - p = testdir.makepyfile("def test_func(): pass") - aaa = testdir.mkpydir("aaa") - test_aaa = aaa.join("test_aaa.py") - p.move(test_aaa) + def test_collect_subdir_event_ordering(self, pytester: Pytester) -> None: + p = pytester.makepyfile("def test_func(): pass") + aaa = pytester.mkpydir("aaa") + test_aaa = aaa.joinpath("test_aaa.py") + p.replace(test_aaa) - items, hookrec = testdir.inline_genitems() + items, hookrec = pytester.inline_genitems() assert len(items) == 1 pprint.pprint(hookrec.calls) hookrec.assert_contains( @@ -549,18 +569,18 @@ class TestSession: ] ) - def test_collect_two_commandline_args(self, testdir): - p = testdir.makepyfile("def test_func(): pass") - aaa = testdir.mkpydir("aaa") - bbb = testdir.mkpydir("bbb") - test_aaa = aaa.join("test_aaa.py") - p.copy(test_aaa) - test_bbb = bbb.join("test_bbb.py") - p.move(test_bbb) + def test_collect_two_commandline_args(self, pytester: Pytester) -> None: + p = pytester.makepyfile("def test_func(): pass") + aaa = pytester.mkpydir("aaa") + bbb = pytester.mkpydir("bbb") + test_aaa = aaa.joinpath("test_aaa.py") + shutil.copy(p, test_aaa) + test_bbb = bbb.joinpath("test_bbb.py") + p.replace(test_bbb) id = "." - items, hookrec = testdir.inline_genitems(id) + items, hookrec = pytester.inline_genitems(id) assert len(items) == 2 pprint.pprint(hookrec.calls) hookrec.assert_contains( @@ -574,26 +594,26 @@ class TestSession: ] ) - def test_serialization_byid(self, testdir): - testdir.makepyfile("def test_func(): pass") - items, hookrec = testdir.inline_genitems() + def test_serialization_byid(self, pytester: Pytester) -> None: + pytester.makepyfile("def test_func(): pass") + items, hookrec = pytester.inline_genitems() assert len(items) == 1 (item,) = items - items2, hookrec = testdir.inline_genitems(item.nodeid) + items2, hookrec = pytester.inline_genitems(item.nodeid) (item2,) = items2 assert item2.name == item.name assert item2.fspath == item.fspath - def test_find_byid_without_instance_parents(self, testdir): - p = testdir.makepyfile( + def test_find_byid_without_instance_parents(self, pytester: Pytester) -> None: + p = pytester.makepyfile( """ class TestClass(object): def test_method(self): pass """ ) - arg = p.basename + "::TestClass::test_method" - items, hookrec = testdir.inline_genitems(arg) + arg = p.name + "::TestClass::test_method" + items, hookrec = pytester.inline_genitems(arg) assert len(items) == 1 (item,) = items assert item.nodeid.endswith("TestClass::test_method") @@ -602,43 +622,45 @@ class TestSession: class Test_getinitialnodes: - def test_global_file(self, testdir, tmpdir) -> None: - x = tmpdir.ensure("x.py") - with tmpdir.as_cwd(): - config = testdir.parseconfigure(x) - col = testdir.getnode(config, x) + def test_global_file(self, pytester: Pytester) -> None: + tmpdir = pytester.path + x = ensure_file(tmpdir / "x.py") + with tmpdir.cwd(): + config = pytester.parseconfigure(x) + col = pytester.getnode(config, x) assert isinstance(col, pytest.Module) assert col.name == "x.py" assert col.parent is not None assert col.parent.parent is None - for col in col.listchain(): - assert col.config is config + for parent in col.listchain(): + assert parent.config is config - def test_pkgfile(self, testdir): + def test_pkgfile(self, pytester: Pytester) -> None: """Verify nesting when a module is within a package. The parent chain should match: Module -> Package -> Session. Session's parent should always be None. """ - tmpdir = testdir.tmpdir - subdir = tmpdir.join("subdir") - x = subdir.ensure("x.py") - subdir.ensure("__init__.py") - with subdir.as_cwd(): - config = testdir.parseconfigure(x) - col = testdir.getnode(config, x) + tmpdir = pytester.path + subdir = tmpdir.joinpath("subdir") + x = ensure_file(subdir / "x.py") + ensure_file(subdir / "__init__.py") + with subdir.cwd(): + config = pytester.parseconfigure(x) + col = pytester.getnode(config, x) + assert col is not None assert col.name == "x.py" assert isinstance(col, pytest.Module) assert isinstance(col.parent, pytest.Package) assert isinstance(col.parent.parent, pytest.Session) # session is batman (has no parents) assert col.parent.parent.parent is None - for col in col.listchain(): - assert col.config is config + for parent in col.listchain(): + assert parent.config is config class Test_genitems: - def test_check_collect_hashes(self, testdir): - p = testdir.makepyfile( + def test_check_collect_hashes(self, pytester: Pytester) -> None: + p = pytester.makepyfile( """ def test_1(): pass @@ -647,8 +669,8 @@ class Test_genitems: pass """ ) - p.copy(p.dirpath(p.purebasename + "2" + ".py")) - items, reprec = testdir.inline_genitems(p.dirpath()) + shutil.copy(p, p.parent / (p.stem + "2" + ".py")) + items, reprec = pytester.inline_genitems(p.parent) assert len(items) == 4 for numi, i in enumerate(items): for numj, j in enumerate(items): @@ -656,8 +678,8 @@ class Test_genitems: assert hash(i) != hash(j) assert i != j - def test_example_items1(self, testdir): - p = testdir.makepyfile( + def test_example_items1(self, pytester: Pytester) -> None: + p = pytester.makepyfile( """ import pytest @@ -674,7 +696,7 @@ class Test_genitems: pass """ ) - items, reprec = testdir.inline_genitems(p) + items, reprec = pytester.inline_genitems(p) assert len(items) == 4 assert items[0].name == "testone" assert items[1].name == "testmethod_one" @@ -682,27 +704,27 @@ class Test_genitems: assert items[3].name == "testmethod_two[.[]" # let's also test getmodpath here - assert items[0].getmodpath() == "testone" - assert items[1].getmodpath() == "TestX.testmethod_one" - assert items[2].getmodpath() == "TestY.testmethod_one" + assert items[0].getmodpath() == "testone" # type: ignore[attr-defined] + assert items[1].getmodpath() == "TestX.testmethod_one" # type: ignore[attr-defined] + assert items[2].getmodpath() == "TestY.testmethod_one" # type: ignore[attr-defined] # PR #6202: Fix incorrect result of getmodpath method. (Resolves issue #6189) - assert items[3].getmodpath() == "TestY.testmethod_two[.[]" + assert items[3].getmodpath() == "TestY.testmethod_two[.[]" # type: ignore[attr-defined] - s = items[0].getmodpath(stopatmodule=False) + s = items[0].getmodpath(stopatmodule=False) # type: ignore[attr-defined] assert s.endswith("test_example_items1.testone") print(s) - def test_class_and_functions_discovery_using_glob(self, testdir): + def test_class_and_functions_discovery_using_glob(self, pytester: Pytester) -> None: """Test that Python_classes and Python_functions config options work as prefixes and glob-like patterns (#600).""" - testdir.makeini( + pytester.makeini( """ [pytest] python_classes = *Suite Test python_functions = *_test test """ ) - p = testdir.makepyfile( + p = pytester.makepyfile( """ class MyTestSuite(object): def x_test(self): @@ -713,13 +735,13 @@ class Test_genitems: pass """ ) - items, reprec = testdir.inline_genitems(p) - ids = [x.getmodpath() for x in items] + items, reprec = pytester.inline_genitems(p) + ids = [x.getmodpath() for x in items] # type: ignore[attr-defined] assert ids == ["MyTestSuite.x_test", "TestCase.test_y"] -def test_matchnodes_two_collections_same_file(testdir): - testdir.makeconftest( +def test_matchnodes_two_collections_same_file(pytester: Pytester) -> None: + pytester.makeconftest( """ import pytest def pytest_configure(config): @@ -751,17 +773,17 @@ def test_matchnodes_two_collections_same_file(testdir): pass """ ) - p = testdir.makefile(".abc", "") - result = testdir.runpytest() + p = pytester.makefile(".abc", "") + result = pytester.runpytest() assert result.ret == 0 result.stdout.fnmatch_lines(["*2 passed*"]) - res = testdir.runpytest("%s::item2" % p.basename) + res = pytester.runpytest("%s::item2" % p.name) res.stdout.fnmatch_lines(["*1 passed*"]) class TestNodekeywords: - def test_no_under(self, testdir): - modcol = testdir.getmodulecol( + def test_no_under(self, pytester: Pytester) -> None: + modcol = pytester.getmodulecol( """ def test_pass(): pass def test_fail(): assert 0 @@ -773,8 +795,8 @@ class TestNodekeywords: assert not x.startswith("_") assert modcol.name in repr(modcol.keywords) - def test_issue345(self, testdir): - testdir.makepyfile( + def test_issue345(self, pytester: Pytester) -> None: + pytester.makepyfile( """ def test_should_not_be_selected(): assert False, 'I should not have been selected to run' @@ -783,17 +805,19 @@ class TestNodekeywords: pass """ ) - reprec = testdir.inline_run("-k repr") + reprec = pytester.inline_run("-k repr") reprec.assertoutcome(passed=1, failed=0) - def test_keyword_matching_is_case_insensitive_by_default(self, testdir): + def test_keyword_matching_is_case_insensitive_by_default( + self, pytester: Pytester + ) -> None: """Check that selection via -k EXPRESSION is case-insensitive. Since markers are also added to the node keywords, they too can be matched without having to think about case sensitivity. """ - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -820,7 +844,7 @@ class TestNodekeywords: ) num_matching_tests = 4 for expression in ("specifictopic", "SPECIFICTOPIC", "SpecificTopic"): - reprec = testdir.inline_run("-k " + expression) + reprec = pytester.inline_run("-k " + expression) reprec.assertoutcome(passed=num_matching_tests, failed=0) @@ -846,11 +870,11 @@ COLLECTION_ERROR_PY_FILES = dict( ) -def test_exit_on_collection_error(testdir): +def test_exit_on_collection_error(pytester: Pytester) -> None: """Verify that all collection errors are collected and no tests executed""" - testdir.makepyfile(**COLLECTION_ERROR_PY_FILES) + pytester.makepyfile(**COLLECTION_ERROR_PY_FILES) - res = testdir.runpytest() + res = pytester.runpytest() assert res.ret == 2 res.stdout.fnmatch_lines( @@ -864,14 +888,16 @@ def test_exit_on_collection_error(testdir): ) -def test_exit_on_collection_with_maxfail_smaller_than_n_errors(testdir): +def test_exit_on_collection_with_maxfail_smaller_than_n_errors( + pytester: Pytester, +) -> None: """ Verify collection is aborted once maxfail errors are encountered ignoring further modules which would cause more collection errors. """ - testdir.makepyfile(**COLLECTION_ERROR_PY_FILES) + pytester.makepyfile(**COLLECTION_ERROR_PY_FILES) - res = testdir.runpytest("--maxfail=1") + res = pytester.runpytest("--maxfail=1") assert res.ret == 1 res.stdout.fnmatch_lines( [ @@ -885,14 +911,16 @@ def test_exit_on_collection_with_maxfail_smaller_than_n_errors(testdir): res.stdout.no_fnmatch_line("*test_03*") -def test_exit_on_collection_with_maxfail_bigger_than_n_errors(testdir): +def test_exit_on_collection_with_maxfail_bigger_than_n_errors( + pytester: Pytester, +) -> None: """ Verify the test run aborts due to collection errors even if maxfail count of errors was not reached. """ - testdir.makepyfile(**COLLECTION_ERROR_PY_FILES) + pytester.makepyfile(**COLLECTION_ERROR_PY_FILES) - res = testdir.runpytest("--maxfail=4") + res = pytester.runpytest("--maxfail=4") assert res.ret == 2 res.stdout.fnmatch_lines( [ @@ -907,14 +935,14 @@ def test_exit_on_collection_with_maxfail_bigger_than_n_errors(testdir): ) -def test_continue_on_collection_errors(testdir): +def test_continue_on_collection_errors(pytester: Pytester) -> None: """ Verify tests are executed even when collection errors occur when the --continue-on-collection-errors flag is set """ - testdir.makepyfile(**COLLECTION_ERROR_PY_FILES) + pytester.makepyfile(**COLLECTION_ERROR_PY_FILES) - res = testdir.runpytest("--continue-on-collection-errors") + res = pytester.runpytest("--continue-on-collection-errors") assert res.ret == 1 res.stdout.fnmatch_lines( @@ -922,7 +950,7 @@ def test_continue_on_collection_errors(testdir): ) -def test_continue_on_collection_errors_maxfail(testdir): +def test_continue_on_collection_errors_maxfail(pytester: Pytester) -> None: """ Verify tests are executed even when collection errors occur and that maxfail is honoured (including the collection error count). @@ -930,18 +958,18 @@ def test_continue_on_collection_errors_maxfail(testdir): test_4 is never executed because the test run is with --maxfail=3 which means it is interrupted after the 2 collection errors + 1 failure. """ - testdir.makepyfile(**COLLECTION_ERROR_PY_FILES) + pytester.makepyfile(**COLLECTION_ERROR_PY_FILES) - res = testdir.runpytest("--continue-on-collection-errors", "--maxfail=3") + res = pytester.runpytest("--continue-on-collection-errors", "--maxfail=3") assert res.ret == 1 res.stdout.fnmatch_lines(["collected 2 items / 2 errors", "*1 failed, 2 errors*"]) -def test_fixture_scope_sibling_conftests(testdir): +def test_fixture_scope_sibling_conftests(pytester: Pytester) -> None: """Regression test case for https://github.com/pytest-dev/pytest/issues/2836""" - foo_path = testdir.mkdir("foo") - foo_path.join("conftest.py").write( + foo_path = pytester.mkdir("foo") + foo_path.joinpath("conftest.py").write_text( textwrap.dedent( """\ import pytest @@ -951,13 +979,13 @@ def test_fixture_scope_sibling_conftests(testdir): """ ) ) - foo_path.join("test_foo.py").write("def test_foo(fix): assert fix == 1") + foo_path.joinpath("test_foo.py").write_text("def test_foo(fix): assert fix == 1") # Tests in `food/` should not see the conftest fixture from `foo/` - food_path = testdir.mkpydir("food") - food_path.join("test_food.py").write("def test_food(fix): assert fix == 1") + food_path = pytester.mkpydir("food") + food_path.joinpath("test_food.py").write_text("def test_food(fix): assert fix == 1") - res = testdir.runpytest() + res = pytester.runpytest() assert res.ret == 1 res.stdout.fnmatch_lines( @@ -969,10 +997,10 @@ def test_fixture_scope_sibling_conftests(testdir): ) -def test_collect_init_tests(testdir): +def test_collect_init_tests(pytester: Pytester) -> None: """Check that we collect files from __init__.py files when they patch the 'python_files' (#3773)""" - p = testdir.copy_example("collect/collect_init_tests") - result = testdir.runpytest(p, "--collect-only") + p = pytester.copy_example("collect/collect_init_tests") + result = pytester.runpytest(p, "--collect-only") result.stdout.fnmatch_lines( [ "collected 2 items", @@ -983,7 +1011,7 @@ def test_collect_init_tests(testdir): " ", ] ) - result = testdir.runpytest("./tests", "--collect-only") + result = pytester.runpytest("./tests", "--collect-only") result.stdout.fnmatch_lines( [ "collected 2 items", @@ -995,7 +1023,7 @@ def test_collect_init_tests(testdir): ] ) # Ignores duplicates with "." and pkginit (#4310). - result = testdir.runpytest("./tests", ".", "--collect-only") + result = pytester.runpytest("./tests", ".", "--collect-only") result.stdout.fnmatch_lines( [ "collected 2 items", @@ -1007,7 +1035,7 @@ def test_collect_init_tests(testdir): ] ) # Same as before, but different order. - result = testdir.runpytest(".", "tests", "--collect-only") + result = pytester.runpytest(".", "tests", "--collect-only") result.stdout.fnmatch_lines( [ "collected 2 items", @@ -1018,23 +1046,23 @@ def test_collect_init_tests(testdir): " ", ] ) - result = testdir.runpytest("./tests/test_foo.py", "--collect-only") + result = pytester.runpytest("./tests/test_foo.py", "--collect-only") result.stdout.fnmatch_lines( ["", " ", " "] ) result.stdout.no_fnmatch_line("*test_init*") - result = testdir.runpytest("./tests/__init__.py", "--collect-only") + result = pytester.runpytest("./tests/__init__.py", "--collect-only") result.stdout.fnmatch_lines( ["", " ", " "] ) result.stdout.no_fnmatch_line("*test_foo*") -def test_collect_invalid_signature_message(testdir): +def test_collect_invalid_signature_message(pytester: Pytester) -> None: """Check that we issue a proper message when we can't determine the signature of a test function (#4026). """ - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -1044,17 +1072,17 @@ def test_collect_invalid_signature_message(testdir): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( ["Could not determine arguments of *.fix *: invalid method signature"] ) -def test_collect_handles_raising_on_dunder_class(testdir): +def test_collect_handles_raising_on_dunder_class(pytester: Pytester) -> None: """Handle proxy classes like Django's LazySettings that might raise on ``isinstance`` (#4266). """ - testdir.makepyfile( + pytester.makepyfile( """ class ImproperlyConfigured(Exception): pass @@ -1072,14 +1100,14 @@ def test_collect_handles_raising_on_dunder_class(testdir): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*1 passed in*"]) assert result.ret == 0 -def test_collect_with_chdir_during_import(testdir): - subdir = testdir.tmpdir.mkdir("sub") - testdir.tmpdir.join("conftest.py").write( +def test_collect_with_chdir_during_import(pytester: Pytester) -> None: + subdir = pytester.mkdir("sub") + pytester.path.joinpath("conftest.py").write_text( textwrap.dedent( """ import os @@ -1088,7 +1116,7 @@ def test_collect_with_chdir_during_import(testdir): % (str(subdir),) ) ) - testdir.makepyfile( + pytester.makepyfile( """ def test_1(): import os @@ -1096,31 +1124,33 @@ def test_collect_with_chdir_during_import(testdir): """ % (str(subdir),) ) - with testdir.tmpdir.as_cwd(): - result = testdir.runpytest() + with pytester.path.cwd(): + result = pytester.runpytest() result.stdout.fnmatch_lines(["*1 passed in*"]) assert result.ret == 0 # Handles relative testpaths. - testdir.makeini( + pytester.makeini( """ [pytest] testpaths = . """ ) - with testdir.tmpdir.as_cwd(): - result = testdir.runpytest("--collect-only") + with pytester.path.cwd(): + result = pytester.runpytest("--collect-only") result.stdout.fnmatch_lines(["collected 1 item"]) -def test_collect_pyargs_with_testpaths(testdir, monkeypatch): - testmod = testdir.mkdir("testmod") +def test_collect_pyargs_with_testpaths( + pytester: Pytester, monkeypatch: MonkeyPatch +) -> None: + testmod = pytester.mkdir("testmod") # NOTE: __init__.py is not collected since it does not match python_files. - testmod.ensure("__init__.py").write("def test_func(): pass") - testmod.ensure("test_file.py").write("def test_func(): pass") + testmod.joinpath("__init__.py").write_text("def test_func(): pass") + testmod.joinpath("test_file.py").write_text("def test_func(): pass") - root = testdir.mkdir("root") - root.ensure("pytest.ini").write( + root = pytester.mkdir("root") + root.joinpath("pytest.ini").write_text( textwrap.dedent( """ [pytest] @@ -1129,32 +1159,32 @@ def test_collect_pyargs_with_testpaths(testdir, monkeypatch): """ ) ) - monkeypatch.setenv("PYTHONPATH", str(testdir.tmpdir), prepend=os.pathsep) - with root.as_cwd(): - result = testdir.runpytest_subprocess() + monkeypatch.setenv("PYTHONPATH", str(pytester.path), prepend=os.pathsep) + with root.cwd(): + result = pytester.runpytest_subprocess() result.stdout.fnmatch_lines(["*1 passed in*"]) -def test_collect_symlink_file_arg(testdir): +def test_collect_symlink_file_arg(pytester: Pytester) -> None: """Collect a direct symlink works even if it does not match python_files (#4325).""" - real = testdir.makepyfile( + real = pytester.makepyfile( real=""" def test_nodeid(request): assert request.node.nodeid == "symlink.py::test_nodeid" """ ) - symlink = testdir.tmpdir.join("symlink.py") + symlink = pytester.path.joinpath("symlink.py") symlink_or_skip(real, symlink) - result = testdir.runpytest("-v", symlink) + result = pytester.runpytest("-v", symlink) result.stdout.fnmatch_lines(["symlink.py::test_nodeid PASSED*", "*1 passed in*"]) assert result.ret == 0 -def test_collect_symlink_out_of_tree(testdir): +def test_collect_symlink_out_of_tree(pytester: Pytester) -> None: """Test collection of symlink via out-of-tree rootdir.""" - sub = testdir.tmpdir.join("sub") - real = sub.join("test_real.py") - real.write( + sub = pytester.mkdir("sub") + real = sub.joinpath("test_real.py") + real.write_text( textwrap.dedent( """ def test_nodeid(request): @@ -1162,14 +1192,13 @@ def test_collect_symlink_out_of_tree(testdir): assert request.node.nodeid == "test_real.py::test_nodeid" """ ), - ensure=True, ) - out_of_tree = testdir.tmpdir.join("out_of_tree").ensure(dir=True) - symlink_to_sub = out_of_tree.join("symlink_to_sub") + out_of_tree = pytester.mkdir("out_of_tree") + symlink_to_sub = out_of_tree.joinpath("symlink_to_sub") symlink_or_skip(sub, symlink_to_sub) - sub.chdir() - result = testdir.runpytest("-vs", "--rootdir=%s" % sub, symlink_to_sub) + os.chdir(sub) + result = pytester.runpytest("-vs", "--rootdir=%s" % sub, symlink_to_sub) result.stdout.fnmatch_lines( [ # Should not contain "sub/"! @@ -1188,30 +1217,31 @@ def test_collect_symlink_dir(pytester: Pytester) -> None: result.assert_outcomes(passed=2) -def test_collectignore_via_conftest(testdir): +def test_collectignore_via_conftest(pytester: Pytester) -> None: """collect_ignore in parent conftest skips importing child (issue #4592).""" - tests = testdir.mkpydir("tests") - tests.ensure("conftest.py").write("collect_ignore = ['ignore_me']") + tests = pytester.mkpydir("tests") + tests.joinpath("conftest.py").write_text("collect_ignore = ['ignore_me']") - ignore_me = tests.mkdir("ignore_me") - ignore_me.ensure("__init__.py") - ignore_me.ensure("conftest.py").write("assert 0, 'should_not_be_called'") + ignore_me = tests.joinpath("ignore_me") + ignore_me.mkdir() + ignore_me.joinpath("__init__.py").touch() + ignore_me.joinpath("conftest.py").write_text("assert 0, 'should_not_be_called'") - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret == ExitCode.NO_TESTS_COLLECTED -def test_collect_pkg_init_and_file_in_args(testdir): - subdir = testdir.mkdir("sub") - init = subdir.ensure("__init__.py") - init.write("def test_init(): pass") - p = subdir.ensure("test_file.py") - p.write("def test_file(): pass") +def test_collect_pkg_init_and_file_in_args(pytester: Pytester) -> None: + subdir = pytester.mkdir("sub") + init = subdir.joinpath("__init__.py") + init.write_text("def test_init(): pass") + p = subdir.joinpath("test_file.py") + p.write_text("def test_file(): pass") # NOTE: without "-o python_files=*.py" this collects test_file.py twice. # This changed/broke with "Add package scoped fixtures #2283" (2b1410895) # initially (causing a RecursionError). - result = testdir.runpytest("-v", str(init), str(p)) + result = pytester.runpytest("-v", str(init), str(p)) result.stdout.fnmatch_lines( [ "sub/test_file.py::test_file PASSED*", @@ -1220,7 +1250,7 @@ def test_collect_pkg_init_and_file_in_args(testdir): ] ) - result = testdir.runpytest("-v", "-o", "python_files=*.py", str(init), str(p)) + result = pytester.runpytest("-v", "-o", "python_files=*.py", str(init), str(p)) result.stdout.fnmatch_lines( [ "sub/__init__.py::test_init PASSED*", @@ -1230,33 +1260,33 @@ def test_collect_pkg_init_and_file_in_args(testdir): ) -def test_collect_pkg_init_only(testdir): - subdir = testdir.mkdir("sub") - init = subdir.ensure("__init__.py") - init.write("def test_init(): pass") +def test_collect_pkg_init_only(pytester: Pytester) -> None: + subdir = pytester.mkdir("sub") + init = subdir.joinpath("__init__.py") + init.write_text("def test_init(): pass") - result = testdir.runpytest(str(init)) + result = pytester.runpytest(str(init)) result.stdout.fnmatch_lines(["*no tests ran in*"]) - result = testdir.runpytest("-v", "-o", "python_files=*.py", str(init)) + result = pytester.runpytest("-v", "-o", "python_files=*.py", str(init)) result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) @pytest.mark.parametrize("use_pkg", (True, False)) -def test_collect_sub_with_symlinks(use_pkg, testdir): +def test_collect_sub_with_symlinks(use_pkg: bool, pytester: Pytester) -> None: """Collection works with symlinked files and broken symlinks""" - sub = testdir.mkdir("sub") + sub = pytester.mkdir("sub") if use_pkg: - sub.ensure("__init__.py") - sub.join("test_file.py").write("def test_file(): pass") + sub.joinpath("__init__.py").touch() + sub.joinpath("test_file.py").write_text("def test_file(): pass") # Create a broken symlink. - symlink_or_skip("test_doesnotexist.py", sub.join("test_broken.py")) + symlink_or_skip("test_doesnotexist.py", sub.joinpath("test_broken.py")) # Symlink that gets collected. - symlink_or_skip("test_file.py", sub.join("test_symlink.py")) + symlink_or_skip("test_file.py", sub.joinpath("test_symlink.py")) - result = testdir.runpytest("-v", str(sub)) + result = pytester.runpytest("-v", str(sub)) result.stdout.fnmatch_lines( [ "sub/test_file.py::test_file PASSED*", @@ -1266,9 +1296,9 @@ def test_collect_sub_with_symlinks(use_pkg, testdir): ) -def test_collector_respects_tbstyle(testdir): - p1 = testdir.makepyfile("assert 0") - result = testdir.runpytest(p1, "--tb=native") +def test_collector_respects_tbstyle(pytester: Pytester) -> None: + p1 = pytester.makepyfile("assert 0") + result = pytester.runpytest(p1, "--tb=native") assert result.ret == ExitCode.INTERRUPTED result.stdout.fnmatch_lines( [ @@ -1283,28 +1313,28 @@ def test_collector_respects_tbstyle(testdir): ) -def test_does_not_eagerly_collect_packages(testdir): - testdir.makepyfile("def test(): pass") - pydir = testdir.mkpydir("foopkg") - pydir.join("__init__.py").write("assert False") - result = testdir.runpytest() +def test_does_not_eagerly_collect_packages(pytester: Pytester) -> None: + pytester.makepyfile("def test(): pass") + pydir = pytester.mkpydir("foopkg") + pydir.joinpath("__init__.py").write_text("assert False") + result = pytester.runpytest() assert result.ret == ExitCode.OK -def test_does_not_put_src_on_path(testdir): +def test_does_not_put_src_on_path(pytester: Pytester) -> None: # `src` is not on sys.path so it should not be importable - testdir.tmpdir.join("src/nope/__init__.py").ensure() - testdir.makepyfile( + ensure_file(pytester.path / "src/nope/__init__.py") + pytester.makepyfile( "import pytest\n" "def test():\n" " with pytest.raises(ImportError):\n" " import nope\n" ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret == ExitCode.OK -def test_fscollector_from_parent(tmpdir, request): +def test_fscollector_from_parent(testdir: Testdir, request: FixtureRequest) -> None: """Ensure File.from_parent can forward custom arguments to the constructor. Context: https://github.com/pytest-dev/pytest-cpp/pull/47 @@ -1320,21 +1350,21 @@ def test_fscollector_from_parent(tmpdir, request): return super().from_parent(parent=parent, fspath=fspath, x=x) collector = MyCollector.from_parent( - parent=request.session, fspath=tmpdir / "foo", x=10 + parent=request.session, fspath=testdir.tmpdir / "foo", x=10 ) assert collector.x == 10 class TestImportModeImportlib: - def test_collect_duplicate_names(self, testdir): + def test_collect_duplicate_names(self, pytester: Pytester) -> None: """--import-mode=importlib can import modules with same names that are not in packages.""" - testdir.makepyfile( + pytester.makepyfile( **{ "tests_a/test_foo.py": "def test_foo1(): pass", "tests_b/test_foo.py": "def test_foo2(): pass", } ) - result = testdir.runpytest("-v", "--import-mode=importlib") + result = pytester.runpytest("-v", "--import-mode=importlib") result.stdout.fnmatch_lines( [ "tests_a/test_foo.py::test_foo1 *", @@ -1343,11 +1373,11 @@ class TestImportModeImportlib: ] ) - def test_conftest(self, testdir): + def test_conftest(self, pytester: Pytester) -> None: """Directory containing conftest modules are not put in sys.path as a side-effect of importing them.""" - tests_dir = testdir.tmpdir.join("tests") - testdir.makepyfile( + tests_dir = pytester.path.joinpath("tests") + pytester.makepyfile( **{ "tests/conftest.py": "", "tests/test_foo.py": """ @@ -1359,13 +1389,13 @@ class TestImportModeImportlib: ), } ) - result = testdir.runpytest("-v", "--import-mode=importlib") + result = pytester.runpytest("-v", "--import-mode=importlib") result.stdout.fnmatch_lines(["* 1 passed in *"]) - def setup_conftest_and_foo(self, testdir): + def setup_conftest_and_foo(self, pytester: Pytester) -> None: """Setup a tests folder to be used to test if modules in that folder can be imported due to side-effects of --import-mode or not.""" - testdir.makepyfile( + pytester.makepyfile( **{ "tests/conftest.py": "", "tests/foo.py": """ @@ -1379,20 +1409,20 @@ class TestImportModeImportlib: } ) - def test_modules_importable_as_side_effect(self, testdir): + def test_modules_importable_as_side_effect(self, pytester: Pytester) -> None: """In import-modes `prepend` and `append`, we are able to import modules from folders containing conftest.py files due to the side effect of changing sys.path.""" - self.setup_conftest_and_foo(testdir) - result = testdir.runpytest("-v", "--import-mode=prepend") + self.setup_conftest_and_foo(pytester) + result = pytester.runpytest("-v", "--import-mode=prepend") result.stdout.fnmatch_lines(["* 1 passed in *"]) - def test_modules_not_importable_as_side_effect(self, testdir): + def test_modules_not_importable_as_side_effect(self, pytester: Pytester) -> None: """In import-mode `importlib`, modules in folders containing conftest.py are not importable, as don't change sys.path or sys.modules as side effect of importing the conftest.py file. """ - self.setup_conftest_and_foo(testdir) - result = testdir.runpytest("-v", "--import-mode=importlib") + self.setup_conftest_and_foo(pytester) + result = pytester.runpytest("-v", "--import-mode=importlib") result.stdout.fnmatch_lines( [ "*ModuleNotFoundError: No module named 'foo'", @@ -1402,29 +1432,29 @@ class TestImportModeImportlib: ) -def test_does_not_crash_on_error_from_decorated_function(testdir: Testdir) -> None: +def test_does_not_crash_on_error_from_decorated_function(pytester: Pytester) -> None: """Regression test for an issue around bad exception formatting due to assertion rewriting mangling lineno's (#4984).""" - testdir.makepyfile( + pytester.makepyfile( """ @pytest.fixture def a(): return 4 """ ) - result = testdir.runpytest() + result = pytester.runpytest() # Not INTERNAL_ERROR assert result.ret == ExitCode.INTERRUPTED -def test_does_not_crash_on_recursive_symlink(testdir: Testdir) -> None: +def test_does_not_crash_on_recursive_symlink(pytester: Pytester) -> None: """Regression test for an issue around recursive symlinks (#7951).""" - symlink_or_skip("recursive", testdir.tmpdir.join("recursive")) - testdir.makepyfile( + symlink_or_skip("recursive", pytester.path.joinpath("recursive")) + pytester.makepyfile( """ def test_foo(): assert True """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret == ExitCode.OK assert result.parseoutcomes() == {"passed": 1}