diff --git a/changelog/4358.removal.rst b/changelog/4358.removal.rst new file mode 100644 index 000000000..1a562cfe7 --- /dev/null +++ b/changelog/4358.removal.rst @@ -0,0 +1,16 @@ +Remove the ``::()`` notation to denote a test class instance in node ids. + +Previously, node ids that contain test instances would use ``::()`` to denote the instance like this:: + + test_foo.py::Test::()::test_bar + +The extra ``::()`` was puzzling to most users and has been removed, so that the test id becomes now:: + + test_foo.py::Test::test_bar + +This change could not accompany a deprecation period as is usual when user-facing functionality changes because +it was not really possible to detect when the functionality was being used explicitly. + +The extra ``::()`` might have been removed in some places internally already, +which then led to confusion in places where it was expected, e.g. with +``--deselect`` (`#4127 `_). diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 0d65dc965..a559b55b5 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -27,7 +27,7 @@ def _splitnode(nodeid): '' 'testing/code' 'testing/code/test_excinfo.py' - 'testing/code/test_excinfo.py::TestFormattedExcinfo::()' + 'testing/code/test_excinfo.py::TestFormattedExcinfo' Return values are lists e.g. [] @@ -39,7 +39,7 @@ def _splitnode(nodeid): # If there is no root node at all, return an empty list so the caller's logic can remain sane return [] parts = nodeid.split(SEP) - # Replace single last element 'test_foo.py::Bar::()' with multiple elements 'test_foo.py', 'Bar', '()' + # Replace single last element 'test_foo.py::Bar' with multiple elements 'test_foo.py', 'Bar' parts[-1:] = parts[-1].split("::") return parts @@ -47,7 +47,7 @@ def _splitnode(nodeid): def ischildnode(baseid, nodeid): """Return True if the nodeid is a child node of the baseid. - E.g. 'foo/bar::Baz::()' is a child of 'foo', 'foo/bar' and 'foo/bar::Baz', but not of 'foo/blorp' + E.g. 'foo/bar::Baz' is a child of 'foo', 'foo/bar' and 'foo/bar::Baz', but not of 'foo/blorp' """ base_parts = _splitnode(baseid) node_parts = _splitnode(nodeid) @@ -107,10 +107,12 @@ class Node(object): self._name2pseudofixturedef = {} if nodeid is not None: + assert "::()" not in nodeid self._nodeid = nodeid else: - assert parent is not None - self._nodeid = self.parent.nodeid + "::" + self.name + self._nodeid = self.parent.nodeid + if self.name != "()": + self._nodeid += "::" + self.name @property def ihook(self): diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 4d4b06d7c..4693be807 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -60,8 +60,7 @@ def pytest_terminal_summary(terminalreporter): tr.write_line("") tr.write_line("(0.00 durations hidden. Use -vv to show these durations.)") break - nodeid = rep.nodeid.replace("::()::", "::") - tr.write_line("%02.2fs %-8s %s" % (rep.duration, rep.when, nodeid)) + tr.write_line("%02.2fs %-8s %s" % (rep.duration, rep.when, rep.nodeid)) def pytest_sessionstart(session): diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index dde2750be..520caf862 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -605,9 +605,7 @@ class TerminalReporter(object): self._tw.line("%s: %d" % (name, count)) else: for item in items: - nodeid = item.nodeid - nodeid = nodeid.replace("::()::", "::") - self._tw.line(nodeid) + self._tw.line(item.nodeid) return stack = [] indent = "" @@ -687,7 +685,7 @@ class TerminalReporter(object): # collect_fspath comes from testid which has a "/"-normalized path if fspath: - res = mkrel(nodeid).replace("::()", "") # parens-normalization + res = mkrel(nodeid) if self.verbosity >= 2 and nodeid.split("::")[0] != fspath.replace( "\\", nodes.SEP ): diff --git a/testing/test_collection.py b/testing/test_collection.py index 3d65b21dc..db2497c0e 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -510,13 +510,8 @@ class TestSession(object): pass """ ) - normid = p.basename + "::TestClass::()::test_method" - for id in [ - p.basename, - p.basename + "::TestClass", - p.basename + "::TestClass::()", - normid, - ]: + normid = p.basename + "::TestClass::test_method" + for id in [p.basename, p.basename + "::TestClass", normid]: items, hookrec = testdir.inline_genitems(id) assert len(items) == 1 assert items[0].name == "test_method" @@ -625,7 +620,7 @@ class TestSession(object): items, hookrec = testdir.inline_genitems(arg) assert len(items) == 1 item, = items - assert item.nodeid.endswith("TestClass::()::test_method") + assert item.nodeid.endswith("TestClass::test_method") # 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"] diff --git a/testing/test_nodes.py b/testing/test_nodes.py index d55184ef2..cc1ee1583 100644 --- a/testing/test_nodes.py +++ b/testing/test_nodes.py @@ -8,11 +8,11 @@ from _pytest import nodes ("", "", True), ("", "foo", True), ("", "foo/bar", True), - ("", "foo/bar::TestBaz::()", True), + ("", "foo/bar::TestBaz", True), ("foo", "food", False), - ("foo/bar::TestBaz::()", "foo/bar", False), - ("foo/bar::TestBaz::()", "foo/bar::TestBop::()", False), - ("foo/bar", "foo/bar::TestBop::()", True), + ("foo/bar::TestBaz", "foo/bar", False), + ("foo/bar::TestBaz", "foo/bar::TestBop", False), + ("foo/bar", "foo/bar::TestBop", True), ), ) def test_ischildnode(baseid, nodeid, expected):