diff --git a/changelog/2535.bugfix.rst b/changelog/2535.bugfix.rst new file mode 100644 index 000000000..ec16e81ea --- /dev/null +++ b/changelog/2535.bugfix.rst @@ -0,0 +1 @@ +Improve error message when test functions of ``unittest.TestCase`` subclasses use a parametrized fixture. diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 068e6814c..b83312b85 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -562,7 +562,20 @@ class FixtureRequest(FuncargnamesCompatAttr): except (AttributeError, ValueError): param = NOTSET param_index = 0 - if fixturedef.params is not None: + has_params = fixturedef.params is not None + fixtures_not_supported = getattr(funcitem, "nofuncargs", False) + if has_params and fixtures_not_supported: + msg = ( + "{name} does not support fixtures, maybe unittest.TestCase subclass?\n" + "Node id: {nodeid}\n" + "Function type: {typename}" + ).format( + name=funcitem.name, + nodeid=funcitem.nodeid, + typename=type(funcitem).__name__, + ) + fail(msg) + if has_params: frame = inspect.stack()[3] frameinfo = inspect.getframeinfo(frame[0]) source_path = frameinfo.filename @@ -571,9 +584,11 @@ class FixtureRequest(FuncargnamesCompatAttr): if source_path.relto(funcitem.config.rootdir): source_path = source_path.relto(funcitem.config.rootdir) msg = ( - "The requested fixture has no parameter defined for the " - "current test.\n\nRequested fixture '{}' defined in:\n{}" + "The requested fixture has no parameter defined for test:\n" + " {}\n\n" + "Requested fixture '{}' defined in:\n{}" "\n\nRequested here:\n{}:{}".format( + funcitem.nodeid, fixturedef.argname, getlocation(fixturedef.func, funcitem.config.rootdir), source_path, diff --git a/testing/example_scripts/unittest/test_parametrized_fixture_error_message.py b/testing/example_scripts/unittest/test_parametrized_fixture_error_message.py new file mode 100644 index 000000000..0b85bef3a --- /dev/null +++ b/testing/example_scripts/unittest/test_parametrized_fixture_error_message.py @@ -0,0 +1,13 @@ +import pytest +import unittest + + +@pytest.fixture(params=[1, 2]) +def two(request): + return request.param + + +@pytest.mark.usefixtures("two") +class TestSomethingElse(unittest.TestCase): + def test_two(self): + pass diff --git a/testing/python/fixture.py b/testing/python/fixture.py index 33c040e31..3b98091ae 100644 --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -3600,7 +3600,8 @@ class TestParameterizedSubRequest(object): result = testdir.runpytest() result.stdout.fnmatch_lines( """ - E*Failed: The requested fixture has no parameter defined for the current test. + E*Failed: The requested fixture has no parameter defined for test: + E* test_call_from_fixture.py::test_foo E* E*Requested fixture 'fix_with_param' defined in: E*test_call_from_fixture.py:4 @@ -3626,7 +3627,8 @@ class TestParameterizedSubRequest(object): result = testdir.runpytest() result.stdout.fnmatch_lines( """ - E*Failed: The requested fixture has no parameter defined for the current test. + E*Failed: The requested fixture has no parameter defined for test: + E* test_call_from_test.py::test_foo E* E*Requested fixture 'fix_with_param' defined in: E*test_call_from_test.py:4 @@ -3656,7 +3658,8 @@ class TestParameterizedSubRequest(object): result = testdir.runpytest() result.stdout.fnmatch_lines( """ - E*Failed: The requested fixture has no parameter defined for the current test. + E*Failed: The requested fixture has no parameter defined for test: + E* test_external_fixture.py::test_foo E* E*Requested fixture 'fix_with_param' defined in: E*conftest.py:4 @@ -3699,7 +3702,8 @@ class TestParameterizedSubRequest(object): result = testdir.runpytest() result.stdout.fnmatch_lines( """ - E*Failed: The requested fixture has no parameter defined for the current test. + E*Failed: The requested fixture has no parameter defined for test: + E* test_foos.py::test_foo E* E*Requested fixture 'fix_with_param' defined in: E*fix.py:4 diff --git a/testing/test_unittest.py b/testing/test_unittest.py index 56fdebf48..c47fce8c9 100644 --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -1010,3 +1010,15 @@ def test_testcase_handles_init_exceptions(testdir): result = testdir.runpytest() assert "should raise this exception" in result.stdout.str() assert "ERROR at teardown of MyTestCase.test_hello" not in result.stdout.str() + + +def test_error_message_with_parametrized_fixtures(testdir): + testdir.copy_example("unittest/test_parametrized_fixture_error_message.py") + result = testdir.runpytest() + result.stdout.fnmatch_lines( + [ + "*test_two does not support fixtures*", + "*TestSomethingElse::test_two", + "*Function type: TestCaseFunction", + ] + )