def test_deferred_hook_checking(pytester: Pytester) -> None: """Check hooks as late as possible (#1821).""" pytester.syspathinsert() pytester.makepyfile( **{ "plugin.py": """ class Hooks(object): def pytest_my_hook(self, config): pass def pytest_configure(config): config.pluginmanager.add_hookspecs(Hooks) """, "conftest.py": """ pytest_plugins = ['plugin'] def pytest_my_hook(config): return 40 """, "test_foo.py": """ def test(request): assert request.config.hook.pytest_my_hook(config=request.config) == [40] """, } ) result = pytester.runpytest() result.stdout.fnmatch_lines(["* 1 passed *"])
def test_filter_traceback_path_no_longer_valid(self, pytester: Pytester) -> None: """Test that filter_traceback() works with the fact that _pytest._code.code.Code.path attribute might return an str object. In this case, one of the files in the traceback no longer exists. This fixes #1133. """ from _pytest._code import filter_traceback pytester.syspathinsert() pytester.makepyfile( filter_traceback_entry_as_str=""" def foo(): raise ValueError """ ) tb = None try: import filter_traceback_entry_as_str filter_traceback_entry_as_str.foo() except ValueError: _, _, tb = sys.exc_info() assert tb is not None pytester.path.joinpath("filter_traceback_entry_as_str.py").unlink() traceback = _pytest._code.Traceback(tb) assert isinstance(traceback[-1].path, str) assert filter_traceback(traceback[-1])
def invocation_path(self, pytester: Pytester) -> Path: pytester.syspathinsert(pytester.path / "src") pytester.chdir() pkg = pytester.path.joinpath("src/pkg") pkg.mkdir(parents=True) pkg.joinpath("__init__.py").touch() pkg.joinpath("test.py").touch() return pytester.path
def test_chained_exceptions_no_reprcrash(self, pytester: Pytester, tw_mock) -> None: """Regression test for tracebacks without a reprcrash (#5971) This happens notably on exceptions raised by multiprocess.pool: the exception transfer from subprocess to main process creates an artificial exception, which ExceptionInfo can't obtain the ReprFileLocation from. """ pytester.makepyfile( """ from concurrent.futures import ProcessPoolExecutor def func(): raise ValueError('value error') def test_a(): with ProcessPoolExecutor() as p: p.submit(func).result() """ ) pytester.syspathinsert() reprec = pytester.inline_run() reports = reprec.getreports("pytest_runtest_logreport") def check_longrepr(longrepr: object) -> None: assert isinstance(longrepr, ExceptionChainRepr) assert len(longrepr.chain) == 2 entry1, entry2 = longrepr.chain tb1, fileloc1, desc1 = entry1 tb2, fileloc2, desc2 = entry2 assert "RemoteTraceback" in str(tb1) assert "ValueError: value error" in str(tb2) assert fileloc1 is None assert fileloc2 is not None assert fileloc2.message == "ValueError: value error" # 3 reports: setup/call/teardown: get the call report assert len(reports) == 3 report = reports[1] assert report.failed check_longrepr(report.longrepr) data = report._to_json() loaded_report = TestReport._from_json(data) assert loaded_report.failed check_longrepr(loaded_report.longrepr) # for same reasons as previous test, ensure we don't blow up here assert loaded_report.longrepr is not None assert isinstance(loaded_report.longrepr, ExceptionChainRepr) loaded_report.longrepr.toterminal(tw_mock)
def test_consider_module(self, pytester: Pytester, pytestpm: PytestPluginManager) -> None: pytester.syspathinsert() pytester.makepyfile(pytest_p1="#") pytester.makepyfile(pytest_p2="#") mod = types.ModuleType("temp") mod.__dict__["pytest_plugins"] = ["pytest_p1", "pytest_p2"] pytestpm.consider_module(mod) assert pytestpm.get_plugin("pytest_p1").__name__ == "pytest_p1" assert pytestpm.get_plugin("pytest_p2").__name__ == "pytest_p2"
def test_newfirst_usecase(self, pytester: Pytester) -> None: pytester.makepyfile( **{ "test_1/test_1.py": """ def test_1(): assert 1 """, "test_2/test_2.py": """ def test_1(): assert 1 """, } ) p1 = pytester.path.joinpath("test_1/test_1.py") os.utime(p1, ns=(p1.stat().st_atime_ns, int(1e9))) result = pytester.runpytest("-v") result.stdout.fnmatch_lines( ["*test_1/test_1.py::test_1 PASSED*", "*test_2/test_2.py::test_1 PASSED*"] ) result = pytester.runpytest("-v", "--nf") result.stdout.fnmatch_lines( ["*test_2/test_2.py::test_1 PASSED*", "*test_1/test_1.py::test_1 PASSED*"] ) p1.write_text("def test_1(): assert 1\n" "def test_2(): assert 1\n") os.utime(p1, ns=(p1.stat().st_atime_ns, int(1e9))) result = pytester.runpytest("--nf", "--collect-only", "-q") result.stdout.fnmatch_lines( [ "test_1/test_1.py::test_2", "test_2/test_2.py::test_1", "test_1/test_1.py::test_1", ] ) # Newest first with (plugin) pytest_collection_modifyitems hook. pytester.makepyfile( myplugin=""" def pytest_collection_modifyitems(items): items[:] = sorted(items, key=lambda item: item.nodeid) print("new_items:", [x.nodeid for x in items]) """ ) pytester.syspathinsert() result = pytester.runpytest("--nf", "-p", "myplugin", "--collect-only", "-q") result.stdout.fnmatch_lines( [ "new_items: *test_1.py*test_1.py*test_2.py*", "test_1/test_1.py::test_2", "test_2/test_2.py::test_1", "test_1/test_1.py::test_1", ] )
def test_import_plugin_dotted_name(self, pytester: Pytester, pytestpm: PytestPluginManager) -> None: pytest.raises(ImportError, pytestpm.import_plugin, "qweqwex.y") pytest.raises(ImportError, pytestpm.import_plugin, "pytest_qweqwex.y") pytester.syspathinsert() pytester.mkpydir("pkg").joinpath("plug.py").write_text("x=3") pluginname = "pkg.plug" pytestpm.import_plugin(pluginname) mod = pytestpm.get_plugin("pkg.plug") assert mod.x == 3
def test_external_test_module_imports_not_cleaned_up( self, pytester: Pytester) -> None: pytester.syspathinsert() pytester.makepyfile(imported="data = 'you son of a silly person'") import imported test_mod = pytester.makepyfile(""" def test_foo(): import imported imported.data = 42""") pytester.inline_run(str(test_mod)) assert imported.data == 42
def test_consider_module_import_module(self, pytester: Pytester, _config_for_test: Config) -> None: pytestpm = _config_for_test.pluginmanager mod = types.ModuleType("x") mod.__dict__["pytest_plugins"] = "pytest_a" aplugin = pytester.makepyfile(pytest_a="#") reprec = pytester.make_hook_recorder(pytestpm) pytester.syspathinsert(aplugin.parent) pytestpm.consider_module(mod) call = reprec.getcall(pytestpm.hook.pytest_plugin_registered.name) assert call.plugin.__name__ == "pytest_a" # check that it is not registered twice pytestpm.consider_module(mod) values = reprec.getcalls("pytest_plugin_registered") assert len(values) == 1
def test_inline_run_taking_and_restoring_a_sys_modules_snapshot( self, pytester: Pytester, monkeypatch: MonkeyPatch) -> None: spy_factory = self.spy_factory() monkeypatch.setattr(pytester_mod, "SysModulesSnapshot", spy_factory) pytester.syspathinsert() original = dict(sys.modules) pytester.makepyfile(import1="# you son of a silly person") pytester.makepyfile(import2="# my hovercraft is full of eels") test_mod = pytester.makepyfile(""" import import1 def test_foo(): import import2""") pytester.inline_run(str(test_mod)) assert len(spy_factory.instances) == 1 spy = spy_factory.instances[0] assert spy._spy_restore_count == 1 assert sys.modules == original assert all(sys.modules[x] is original[x] for x in sys.modules)
def test_import_plugin_importname(self, pytester: Pytester, pytestpm: PytestPluginManager) -> None: pytest.raises(ImportError, pytestpm.import_plugin, "qweqwex.y") pytest.raises(ImportError, pytestpm.import_plugin, "pytest_qweqwx.y") pytester.syspathinsert() pluginname = "pytest_hello" pytester.makepyfile(**{pluginname: ""}) pytestpm.import_plugin("pytest_hello") len1 = len(pytestpm.get_plugins()) pytestpm.import_plugin("pytest_hello") len2 = len(pytestpm.get_plugins()) assert len1 == len2 plugin1 = pytestpm.get_plugin("pytest_hello") assert plugin1.__name__.endswith("pytest_hello") plugin2 = pytestpm.get_plugin("pytest_hello") assert plugin2 is plugin1
def test_consider_env_plugin_instantiation( self, pytester: Pytester, monkeypatch: MonkeyPatch, pytestpm: PytestPluginManager, ) -> None: pytester.syspathinsert() pytester.makepyfile(xy123="#") monkeypatch.setitem(os.environ, "PYTEST_PLUGINS", "xy123") l1 = len(pytestpm.get_plugins()) pytestpm.consider_env() l2 = len(pytestpm.get_plugins()) assert l2 == l1 + 1 assert pytestpm.get_plugin("xy123") pytestpm.consider_env() l3 = len(pytestpm.get_plugins()) assert l2 == l3
def test_early_load_setuptools_name( self, pytester: Pytester, monkeypatch, load_cov_early ) -> None: monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD") pytester.makepyfile(mytestplugin1_module="") pytester.makepyfile(mytestplugin2_module="") pytester.makepyfile(mycov_module="") pytester.syspathinsert() loaded = [] @attr.s class DummyEntryPoint: name = attr.ib() module = attr.ib() group = "pytest11" def load(self): __import__(self.module) loaded.append(self.name) return sys.modules[self.module] entry_points = [ DummyEntryPoint("myplugin1", "mytestplugin1_module"), DummyEntryPoint("myplugin2", "mytestplugin2_module"), DummyEntryPoint("mycov", "mycov_module"), ] @attr.s class DummyDist: entry_points = attr.ib() files = () def my_dists(): return (DummyDist(entry_points),) monkeypatch.setattr(importlib_metadata, "distributions", my_dists) params = ("-p", "mycov") if load_cov_early else () pytester.runpytest_inprocess(*params) if load_cov_early: assert loaded == ["mycov", "myplugin1", "myplugin2"] else: assert loaded == ["myplugin1", "myplugin2", "mycov"]
def test_importplugin_error_message(pytester: Pytester, pytestpm: PytestPluginManager) -> None: """Don't hide import errors when importing plugins and provide an easy to debug message. See #375 and #1998. """ pytester.syspathinsert(pytester.path) pytester.makepyfile(qwe="""\ def test_traceback(): raise ImportError('Not possible to import: ☺') test_traceback() """) with pytest.raises(ImportError) as excinfo: pytestpm.import_plugin("qwe") assert str(excinfo.value).endswith( 'Error importing plugin "qwe": Not possible to import: ☺') assert "in test_traceback" in str(excinfo.traceback[-1])
def test_issue4445_import_plugin(self, pytester: Pytester, capwarn) -> None: """#4445: Make sure the warning points to a reasonable location""" pytester.makepyfile(some_plugin=""" import pytest pytest.skip("thing", allow_module_level=True) """) pytester.syspathinsert() pytester.parseconfig("-p", "some_plugin") # with stacklevel=2 the warning should originate from # config.PytestPluginManager.import_plugin is thrown by a skipped plugin assert len(capwarn.captured) == 1 warning, location = capwarn.captured.pop() file, _, func = location assert "skipped plugin 'some_plugin': thing" in str(warning.message) assert f"config{os.sep}__init__.py" in file assert func == "_warn_about_skipped_plugins"
def test_group_warnings_by_message_summary(pytester: Pytester) -> None: pytester.copy_example("warnings/test_group_warnings_by_message_summary") pytester.syspathinsert() result = pytester.runpytest() result.stdout.fnmatch_lines( [ "*== %s ==*" % WARNINGS_SUMMARY_HEADER, "test_1.py: 21 warnings", "test_2.py: 1 warning", " */test_1.py:7: UserWarning: foo", " warnings.warn(UserWarning(msg))", "", "test_1.py: 20 warnings", " */test_1.py:7: UserWarning: bar", " warnings.warn(UserWarning(msg))", "", "-- Docs: *", "*= 42 passed, 42 warnings *", ], consecutive=True, )
def pyfile_with_warnings(pytester: Pytester, request: FixtureRequest) -> str: """Create a test file which calls a function in a module which generates warnings.""" pytester.syspathinsert() test_name = request.function.__name__ module_name = test_name.lstrip("test_") + "_module" test_file = pytester.makepyfile( """ import {module_name} def test_func(): assert {module_name}.foo() == 1 """.format(module_name=module_name), **{ module_name: """ import warnings def foo(): warnings.warn(UserWarning("user warning")) warnings.warn(RuntimeWarning("runtime warning")) return 1 """, }, ) return str(test_file)
def test_external_plugins_integrated(pytester: Pytester, plugin) -> None: pytester.syspathinsert() pytester.makepyfile(**{plugin: ""}) with pytest.warns(pytest.PytestConfigWarning): pytester.parseconfig("-p", plugin)