pytest: TestCase.setUpClass is not called in some cases in pytest>=6.2.0
- a detailed description of the bug or problem you are having
- output of
pip list
from the virtual environment you are using - pytest and operating system versions:
pytest>=6.2.0,<=6.2.2
- minimal example if possible
python version: 3.8.7 (v3.8.7:6503f05dd5, Dec 21 2020, 12:45:15) [Clang 6.0 (clang-600.0.57)]
platform: macOS-10.16-x86_64-i386-64bit
The problem
pytest
doesn’t call setUpClass, if test method has /
in its name. Test method itself is executed.
In example below setUpClass
is executed only after running first test method. If there wasn’t another test method without /
in its name, setUpClass
wouldn’t be called at all.
from unittest import TestCase
class BaseTest(TestCase):
def __init_subclass__(cls, **kwargs):
def test_method(self):
self.assertEqual('foo', self.setup_class_attr)
# / symbol on the next line causes the problem
setattr(cls, "test_setup_class_attr['/foo']", test_method)
setattr(cls, "test_setup_class_attr['foo']", test_method)
@classmethod
def setUpClass(cls):
cls.setup_class_attr = 'foo'
class Test(BaseTest):
...
Running with unittests (gives expected results):
Ran 2 tests in 0.005s
OK
Running with pytest<6.2.0 (gives expected results):
$ pytest
====================================== test session starts ======================================
platform darwin -- Python 3.8.7, pytest-6.1.2, py-1.10.0, pluggy-0.13.1
rootdir: /Users/rocky/py/test
collected 2 items
test_setup_cls.py .. [100%]
======================================= 2 passed in 0.04s =======================================
Running with pytest >=6.2.0:
$ pytest
====================================== test session starts =======================================
platform darwin -- Python 3.8.7, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /Users/rocky/py/test
collected 2 items
test_setup_cls.py F. [100%]
============================================ FAILURES ============================================
_______________________________ Test.test_setup_class_attr['/foo'] _______________________________
self = <test_setup_cls.Test testMethod=test_setup_class_attr['/foo']>
def test_method(self):
> self.assertEqual('foo', self.setup_class_attr)
E AttributeError: 'Test' object has no attribute 'setup_class_attr'
test_setup_cls.py:7: AttributeError
==================================== short test summary info =====================================
FAILED test_setup_cls.py::Test::test_setup_class_attr['/foo'] - AttributeError: 'Test' object h...
================================== 1 failed, 1 passed in 0.20s ===================================
$ pip list
Package Version
---------- -------
attrs 20.3.0
iniconfig 1.1.1
packaging 20.9
pip 21.0.1
pluggy 0.13.1
py 1.10.0
pyparsing 2.4.7
pytest 6.2.2
setuptools 54.2.0
toml 0.10.2
About this issue
- Original URL
- State: open
- Created 3 years ago
- Comments: 17 (17 by maintainers)
Commits related to this issue
- Merge #118 118: Update pytest to 7.0.0 r=aragilar a=pyup-bot This PR updates [pytest](https://pypi.org/project/pytest) from **6.2.4** to **7.0.0**. <details> <summary>Changelog</summary> ... — committed to aragilar/stringtopy by bors[bot] 2 years ago
- Merge #33 #34 33: Update pytest to 7.1.1 r=aragilar a=pyup-bot This PR updates [pytest](https://pypi.org/project/pytest) from **6.2.4** to **7.1.1**. <details> <summary>Changelog</summary> ... — committed to aragilar/pytest-info-collector by bors[bot] 2 years ago
- Merge #33 #34 33: Update pytest to 7.1.1 r=aragilar a=pyup-bot This PR updates [pytest](https://pypi.org/project/pytest) from **6.2.4** to **7.1.1**. <details> <summary>Changelog</summary> ... — committed to aragilar/pytest-info-collector by bors[bot] 2 years ago
- Merge #29 29: Update pytest to 7.1.1 r=aragilar a=pyup-bot This PR updates [pytest](https://pypi.org/project/pytest) from **6.2.4** to **7.1.1**. <details> <summary>Changelog</summary> ... — committed to aragilar/spaceplot by bors[bot] 2 years ago
- Merge #33 33: Update pytest to 7.1.1 r=aragilar a=pyup-bot This PR updates [pytest](https://pypi.org/project/pytest) from **6.2.4** to **7.1.1**. <details> <summary>Changelog</summary> ... — committed to aragilar/pytest-info-collector by bors[bot] 2 years ago
- Merge #128 128: Update pytest to 7.1.1 r=aragilar a=pyup-bot This PR updates [pytest](https://pypi.org/project/pytest) from **6.2.4** to **7.1.1**. <details> <summary>Changelog</summary> ... — committed to aragilar/stringtopy by bors[bot] 2 years ago
- Merge #35 #36 35: Update sphinx_rtd_theme to 1.0.0 r=aragilar a=pyup-bot This PR updates [sphinx_rtd_theme](https://pypi.org/project/sphinx_rtd_theme) from **0.5.2** to **1.0.0**. *The bot wasn'... — committed to aragilar/pytest-info-collector by bors[bot] 2 years ago
- Merge #31 31: Update pytest to 7.1.1 r=aragilar a=pyup-bot This PR updates [pytest](https://pypi.org/project/pytest) from **6.2.4** to **7.1.1**. <details> <summary>Changelog</summary> ... — committed to aragilar/spaceplot by bors[bot] 2 years ago
- chore(deps): update dependency pytest to v7 (#3) This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [pytest](https://docs.pytest.org/en/latest/) ([sourc... — committed to jaypikay/doxy by deleted user a year ago
@parthdpa Sure! I’ll try to write some things to get you started.
Nodeids
The pytest collection phase creates a tree of nodes. If you have a test suite, you can see the tree by running
pytest --collect-only
.Each node has a parent (exception
Session
, the root node) and nodeid. The nodeid of some test might look like this:This is the nodeid of test
test_repr_source
in test classTestFormattedExcinfo
in filetesting/code/test_excinfo.py
.The parent of this node is the node with nodeid
which represents the test class, whose parent in turn is
testing/code/test_excinfo.py
, whose parent istesting/code
and so on, until we reachSession
whose nodeid is the empty string""
(without the quotes).Generally the path parts are separated by
/
and the further parts are separated by::
.Nodeid parents
Some code wants to know whether one nodeid is the parent of another nodeid. For example, if
testing/code
defines anautouse=True
fixture, then it should affect its children nodes, but not others.Previously this was done with an
ischildnode
method, but this turned out to be slow in some cases, so commit aa0e2d654fb0c8ac18747fe1cdf54d8f29bcd24a changed it such that aiterparentnodeids
function computes all of the parent nodeids of a given node, which is then faster to check for containment.New code
The code for
iterparentnodeids
uses the following rule: “Note that :: parts are only considered at the last / component.”. I.e.so in the case of
The path part is still
testing::foo/code/test_excinfo.py
.Problem
The problem is that it broke @MrMrRobat case:
The path part here is taken according to the rule above as
x.py::Test::test_setup_class_attr[/
(up to the first/
). But what was really intended is for the path part to be justx.py
.Solution
Well that’s where you come in 😃
Here are some possible solutions from the top of my head:
::
in paths./ parts are only considered at the first :: component
, i.e. give precedence to::
. This will break::
in paths which may be fine.[]
brackets in some special way, as @RonnyPfannschmidt suggests.Personally I wouldn’t want to do 4, I think static meaning is very good to have.
I would try 2 and see if it breaks anything, maybe that would be enough.
Method with
/
in the name, I guess in pytest you do see everything 😀Thanks for the bisect @nicoddemus, I’ll take a look soon.
@bluetech a possible initial quick-fix could be to split them out of parameter names (via ‘[’)