diff --git a/_pytest/fixtures.py b/_pytest/fixtures.py index da84ab429..bfae4cb61 100644 --- a/_pytest/fixtures.py +++ b/_pytest/fixtures.py @@ -1029,7 +1029,7 @@ class FixtureManager(object): session.config.pluginmanager.register(self, "funcmanage") def getfixtureinfo(self, node, func, cls, funcargs=True): - if funcargs and not hasattr(node, "nofuncargs"): + if funcargs and not getattr(node, "nofuncargs", False): argnames = getfuncargnames(func, cls=cls) else: argnames = () diff --git a/_pytest/junitxml.py b/_pytest/junitxml.py index dcd216ada..0fd466d06 100644 --- a/_pytest/junitxml.py +++ b/_pytest/junitxml.py @@ -231,7 +231,8 @@ class _NodeReporter(object): message=skipreason, ) ) - self.write_captured_output(report) + self.write_captured_output(report) + def finalize(self): data = self.to_xml().unicode(indent=0) diff --git a/_pytest/python.py b/_pytest/python.py index 05988877d..48516199f 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -1263,7 +1263,7 @@ class Function(FunctionMixin, nodes.Item, fixtures.FuncargnamesCompatAttr): if fixtureinfo is None: fixtureinfo = self.session._fixturemanager.getfixtureinfo( - self.parent, self.obj, self.cls, funcargs=not self._isyieldedfunction() + self, self.obj, self.cls, funcargs=not self._isyieldedfunction() ) self._fixtureinfo = fixtureinfo self.fixturenames = fixtureinfo.names_closure diff --git a/_pytest/unittest.py b/_pytest/unittest.py index 49d6c1e3d..6ad9fda88 100644 --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -55,7 +55,7 @@ class UnitTestCase(Class): continue funcobj = getattr(x, "im_func", x) transfer_markers(funcobj, cls, module) - yield TestCaseFunction(name, parent=self) + yield TestCaseFunction(name, parent=self, callobj=funcobj) foundsomething = True if not foundsomething: @@ -67,6 +67,7 @@ class UnitTestCase(Class): class TestCaseFunction(Function): + nofuncargs = True _excinfo = None def setup(self): diff --git a/changelog/3491.bugfix.rst b/changelog/3491.bugfix.rst new file mode 100644 index 000000000..2ac733cbc --- /dev/null +++ b/changelog/3491.bugfix.rst @@ -0,0 +1 @@ +Fixed a bug where stdout and stderr were logged twice by junitxml when a test was marked xfail. \ No newline at end of file diff --git a/changelog/3498.bugfix b/changelog/3498.bugfix new file mode 100644 index 000000000..344038bee --- /dev/null +++ b/changelog/3498.bugfix @@ -0,0 +1 @@ +Fix ``usefixtures`` mark applyed to unittest tests by correctly instantiating ``FixtureInfo``. diff --git a/doc/en/goodpractices.rst b/doc/en/goodpractices.rst index d9f0fff11..1e22c10f2 100644 --- a/doc/en/goodpractices.rst +++ b/doc/en/goodpractices.rst @@ -174,7 +174,7 @@ Note that this layout also works in conjunction with the ``src`` layout mentione .. _`use tox`: -Tox +tox ------ For development, we recommend to use virtualenv_ environments and pip_ @@ -194,7 +194,7 @@ Once you are done with your work and want to make sure that your actual package passes all tests you may want to look into `tox`_, the virtualenv test automation tool and its `pytest support `_. -Tox helps you to setup virtualenv environments with pre-defined +tox helps you to setup virtualenv environments with pre-defined dependencies and then executing a pre-configured test command with options. It will run tests against the installed package and not against your source code checkout, helping to detect packaging diff --git a/doc/en/nose.rst b/doc/en/nose.rst index 72d18c30f..1f7b7b638 100644 --- a/doc/en/nose.rst +++ b/doc/en/nose.rst @@ -58,7 +58,7 @@ Unsupported idioms / known issues You may find yourself wanting to do this if you ran ``python setup.py install`` to set up your project, as opposed to ``python setup.py develop`` or any of the package manager equivalents. Installing with develop in a - virtual environment like Tox is recommended over this pattern. + virtual environment like tox is recommended over this pattern. - nose-style doctests are not collected and executed correctly, also doctest fixtures don't work. diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index a1a6ccc00..6522abf13 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -503,6 +503,23 @@ class TestPython(object): fnode.assert_attr(message="expected test failure") # assert "ValueError" in fnode.toxml() + def test_xfail_captures_output_once(self, testdir): + testdir.makepyfile(""" + import sys + import pytest + + @pytest.mark.xfail() + def test_fail(): + sys.stdout.write('XFAIL This is stdout') + sys.stderr.write('XFAIL This is stderr') + assert 0 + """) + result, dom = runandparse(testdir) + node = dom.find_first_by_tag("testsuite") + tnode = node.find_first_by_tag("testcase") + assert len(tnode.find_by_tag('system-err')) == 1 + assert len(tnode.find_by_tag('system-out')) == 1 + def test_xfailure_xpass(self, testdir): testdir.makepyfile( """ diff --git a/testing/test_unittest.py b/testing/test_unittest.py index ab8ec927a..f5728c781 100644 --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -925,3 +925,64 @@ def test_class_method_containing_test_issue1558(testdir): ) reprec = testdir.inline_run() reprec.assertoutcome(passed=1) + + +@pytest.mark.issue(3498) +@pytest.mark.parametrize("base", [ + 'six.moves.builtins.object', + 'unittest.TestCase', + 'unittest2.TestCase', +]) +def test_usefixtures_marker_on_unittest(base, testdir): + module = base.rsplit('.', 1)[0] + pytest.importorskip(module) + testdir.makepyfile(conftest=""" + import pytest + + @pytest.fixture(scope='function') + def fixture1(request, monkeypatch): + monkeypatch.setattr(request.instance, 'fixture1', True ) + + + @pytest.fixture(scope='function') + def fixture2(request, monkeypatch): + monkeypatch.setattr(request.instance, 'fixture2', True ) + + def node_and_marks(item): + print(item.nodeid) + for mark in item.iter_markers(): + print(" ", mark) + + @pytest.fixture(autouse=True) + def my_marks(request): + node_and_marks(request.node) + + def pytest_collection_modifyitems(items): + for item in items: + node_and_marks(item) + + """) + + testdir.makepyfile(""" + import pytest + import {module} + + class Tests({base}): + fixture1 = False + fixture2 = False + + @pytest.mark.usefixtures("fixture1") + def test_one(self): + assert self.fixture1 + assert not self.fixture2 + + @pytest.mark.usefixtures("fixture1", "fixture2") + def test_two(self): + assert self.fixture1 + assert self.fixture2 + + + """.format(module=module, base=base)) + + result = testdir.runpytest('-s') + result.assert_outcomes(passed=2)