diff --git a/CHANGELOG b/CHANGELOG index 78c7e4f25..629e99199 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -35,6 +35,8 @@ Changes between 2.2.4 and 2.3.0.dev - fix issue128: show captured output when capsys/capfd are used +- fix issue179: propperly show the dependency chain of factories + - pluginmanager.register(...) now raises ValueError if the plugin has been already registered or the name is taken diff --git a/_pytest/python.py b/_pytest/python.py index 7dd356001..edb61a457 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -781,7 +781,7 @@ def raises(ExpectedException, *args, **kwargs): # we want to catch a AssertionError # replace our subclass with the builtin one # see https://bitbucket.org/hpk42/pytest/issue/176/pytestraises - from exceptions import AssertionError as ExpectedException + from _pytest.assertion.util import BuiltinAssertionError as ExpectedException if not args: return RaisesContext(ExpectedException) @@ -1211,8 +1211,14 @@ class FuncargLookupErrorRepr(TerminalRepr): def toterminal(self, tw): tw.line() - for line in self.factblines or []: - tw.line(line) + if self.factblines: + tw.line(' dependency of:') + for factorydef in self.factblines: + tw.line(' %s in %s' % ( + factorydef.argname, + factorydef.baseid, + )) + tw.line() for line in self.deflines: tw.line(" " + line.strip()) for line in self.errorstring.split("\n"): @@ -1308,16 +1314,14 @@ class FuncargManager: obj = getattr(holderobj, name) if not callable(obj): continue - # to avoid breaking on magic global callables - # we explicitly check if we get a sane code object - # else having mock.call in the globals fails for example - code = py.code.getrawcode(obj) - if not inspect.iscode(code): - continue # resource factories either have a pytest_funcarg__ prefix # or are "funcarg" marked marker = getattr(obj, "_pytestfactory", None) if marker is not None: + if not isinstance(marker, FactoryMarker): + # magic globals with __getattr__ + # give us something thats wrong for that case + continue assert not name.startswith(self._argprefix) argname = name scope = marker.scope diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 5c30487d0..0e12b74d6 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -162,7 +162,7 @@ class TestPython: import pytest @pytest.mark.parametrize('arg1', "<&'", ids="<&'") def test_func(arg1): - print arg1 + print(arg1) assert 0 """) result, dom = runandparse(testdir) diff --git a/testing/test_python.py b/testing/test_python.py index c3cd5dfb1..9960fe700 100644 --- a/testing/test_python.py +++ b/testing/test_python.py @@ -1422,9 +1422,9 @@ class TestRaises: def test_raises_flip_builtin_AssertionError(self): # we replace AssertionError on python level # however c code might still raise the builtin one - import exceptions + from _pytest.assertion.util import BuiltinAssertionError pytest.raises(AssertionError,""" - raise exceptions.AssertionError + raise BuiltinAssertionError """) @pytest.mark.skipif('sys.version < "2.5"') @@ -1664,6 +1664,30 @@ class TestFuncargFactory: "*2 passed*" ]) + def test_factory_uses_unknown_funcarg_as_dependency_error(self, testdir): + testdir.makepyfile(""" + import pytest + + @pytest.factory() + def fail(missing): + return + + @pytest.factory() + def call_fail(fail): + return + + def test_missing(call_fail): + pass + """) + result = testdir.runpytest() + result.stdout.fnmatch_lines([ + "*dependency of:*", + "*call_fail*", + "*def fail(*", + "*LookupError: no factory found for argument 'missing'", + ]) + + class TestResourceIntegrationFunctional: def test_parametrize_with_ids(self, testdir):