pytest runs imported unittest.TestCase for the second time after it had already called testDownClass
See original GitHub issuea detailed description of the bug or suggestion
Test class (inheriting from unittest.TestCase
) imported into another test file gets executed for the second time, after its tearDownClass
method had already completed.
There are two solutions I can think of now:
-
Never execute test classes that were imported from another module. Execute only those test classes that are defined in the current module.
-
Execute
setUpClass
andtestDownClass
(and similar methods) for each unique pair of class name and its module name. Now it seems that individual test methods are executed in this fashion, but thesetUpClass
andtestDownClass
methods are executed once per class name only.
Either approach should be possible to be implemented by inspecting test_cls.__module__
value.
Personally, I am leaning towards 1st approach as it is what I was expecting to be happening.
I am also providing an example use case for importing one test class into another test class’s module in the form of a Test1.create_state
helper method that is used by both Test1
and Test2
. (See minimal below).
output of pip list
from the virtual environment you are using
> conda list
#
# Name Version Build Channel
_libgcc_mutex 0.1 conda_forge conda-forge
_openmp_mutex 4.5 0_gnu conda-forge
astroid 2.3.3 py36_1 conda-forge
attrs 19.3.0 py_0 conda-forge
blas 1.0 mkl
bumpversion 0.5.3 pypi_0 pypi
ca-certificates 2020.4.5.1 hecc5488_0 conda-forge
certifi 2020.4.5.1 py36h9f0ad1d_0 conda-forge
coverage 5.0.3 py36h516909a_0 conda-forge
cycler 0.10.0 py_2 conda-forge
dbus 1.13.6 he372182_0 conda-forge
expat 2.2.9 he1b5a44_2 conda-forge
fontconfig 2.13.1 h86ecdb6_1001 conda-forge
freetype 2.10.0 he983fc9_1 conda-forge
gettext 0.19.8.1 hc5be6a0_1002 conda-forge
glib 2.58.3 py36hd3ed26a_1004 conda-forge
gst-plugins-base 1.14.5 h0935bb2_2 conda-forge
gstreamer 1.14.5 h36ae1b5_2 conda-forge
icu 64.2 he1b5a44_1 conda-forge
importlib_metadata 1.5.0 py36_0 conda-forge
intel-openmp 2019.4 243
isort 4.3.21 py36_0 conda-forge
jinja2 2.10.3 py_0 conda-forge
joblib 0.14.1 py_0 conda-forge
jpeg 9c h14c3975_1001 conda-forge
kiwisolver 1.1.0 py36hc9558a2_0 conda-forge
lazy-object-proxy 1.4.3 py36h516909a_1 conda-forge
ld_impl_linux-64 2.34 h53a641e_0 conda-forge
libblas 3.8.0 15_openblas conda-forge
libcblas 3.8.0 15_openblas conda-forge
libclang 9.0.1 default_hde54327_0 conda-forge
libcxx 9.0.1 1 conda-forge
libcxxabi 9.0.1 1 conda-forge
libedit 3.1.20181209 hc058e9b_0
libffi 3.2.1 he1b5a44_1006 conda-forge
libgcc-ng 9.2.0 h24d8f2e_2 conda-forge
libgfortran 3.0.0 1 conda-forge
libgfortran-ng 7.3.0 hdf63c60_5 conda-forge
libgomp 9.2.0 h24d8f2e_2 conda-forge
libiconv 1.15 h516909a_1006 conda-forge
liblapack 3.8.0 15_openblas conda-forge
libllvm9 9.0.1 hc9558a2_0 conda-forge
libopenblas 0.3.8 h5ec1e0e_0 conda-forge
libpng 1.6.37 hed695b0_0 conda-forge
libstdcxx-ng 9.2.0 hdf63c60_2 conda-forge
libuuid 2.32.1 h14c3975_1000 conda-forge
libxcb 1.13 h14c3975_1002 conda-forge
libxkbcommon 0.10.0 he1b5a44_0 conda-forge
libxml2 2.9.10 hee79883_0 conda-forge
llvm-openmp 9.0.1 hc9558a2_2 conda-forge
markupsafe 1.1.1 py36h516909a_0 conda-forge
matplotlib 3.1.1 py36_2 conda-forge
matplotlib-base 3.1.1 py36h250f245_2 conda-forge
mccabe 0.6.1 py_1 conda-forge
mkl 2019.4 243
mkl-service 2.3.0 py36h516909a_0 conda-forge
mkl_fft 1.1.0 py36hc1659b7_1 conda-forge
mkl_random 1.1.0 py36hb3f55d8_0 conda-forge
more-itertools 8.2.0 py_0 conda-forge
mypy 0.670 pypi_0 pypi
mypy-extensions 0.4.3 pypi_0 pypi
ncurses 6.1 hf484d3e_1002 conda-forge
nspr 4.25 he1b5a44_0 conda-forge
nss 3.47 he751ad9_0 conda-forge
numpy 1.17.4 py36hc1035e2_0
numpy-base 1.17.4 py36hde5b4d6_0
openblas 0.3.8 he1df0ab_0 conda-forge
openssl 1.1.1f h516909a_0 conda-forge
packaging 20.1 py_0 conda-forge
pandas 0.25.3 py36hb3f55d8_0 conda-forge
patsy 0.5.1 py_0 conda-forge
pcre 8.44 he1b5a44_0 conda-forge
pip 20.0.2 py_2 conda-forge
pluggy 0.12.0 py_0 conda-forge
pthread-stubs 0.4 h14c3975_1001 conda-forge
py 1.8.1 py_0 conda-forge
py4j 0.10.6 py36_1 conda-forge
pylint 2.4.4 py36_0 conda-forge
pyparsing 2.4.6 py_0 conda-forge
pyqt 5.12.3 py36hcca6a23_1 conda-forge
pyqt5-sip 4.19.18 pypi_0 pypi
pyqtwebengine 5.12.1 pypi_0 pypi
pyspark 2.3.0 py36_0 conda-forge
pytest 5.4.1 pypi_0 pypi
python 3.6.10 h0371630_0
python-dateutil 2.8.1 py_0 conda-forge
python_abi 3.6 1_cp36m conda-forge
pytz 2019.3 py_0 conda-forge
qt 5.12.5 hd8c4c69_1 conda-forge
readline 7.0 hf8c457e_1001 conda-forge
scikit-learn 0.22.1 py36hd81dba3_0
scipy 1.3.2 py36h7c811a0_0
setuptools 45.2.0 py36_0 conda-forge
six 1.14.0 py36_0 conda-forge
sqlite 3.31.1 h7b6447c_0
statsmodels 0.10.1 py36hc1659b7_2 conda-forge
tk 8.6.10 hed695b0_0 conda-forge
tornado 6.0.3 py36h516909a_4 conda-forge
typed-ast 1.3.5 pypi_0 pypi
typing-extensions 3.7.4.1 pypi_0 pypi
wcwidth 0.1.8 py_0 conda-forge
wheel 0.34.2 py_1 conda-forge
wrapt 1.12.0 py36h516909a_0 conda-forge
xorg-libxau 1.0.9 h14c3975_0 conda-forge
xorg-libxdmcp 1.1.3 h516909a_0 conda-forge
xz 5.2.4 h14c3975_1001 conda-forge
yapf 0.28.0 py_0 conda-forge
zipp 3.0.0 py_0 conda-forge
zlib 1.2.11 h516909a_1006 conda-forge
pytest and operating system versions
> lsb_release -a
LSB Version: :core-4.1-amd64:core-4.1-noarch
Distributor ID: Fedora
Description: Fedora release 30 (Thirty)
Release: 30
Codename: Thirty
> pytest --version
This is pytest version 5.4.1
minimal
How to reproduce:
- Create a directory with the 2 test files (attached below).
cd
to that directory and runpytest
(so that both files are collected and executed).
Actual result:
test_1.py .
test_2.py F.
The failure above is Test1.test_it
being executed for the second time after Test1.testDownClass
had already been called and, thus, self.state
is set to None
.
Expected result (as per 1st approach outlined above):
test_1.py .
test_2.py .
Files
- test_1.py
from unittest import TestCase class Test1(TestCase): @classmethod def setUpClass(cls) -> None: cls.state = cls.create_state() @classmethod def tearDownClass(cls) -> None: cls.state = None def test_it(self): assert self.state is not None @classmethod def create_state(cls) -> str: return "state123"
- test_2.py
from unittest import TestCase from test_1 import Test1 class Test2(TestCase): @classmethod def setUpClass(cls) -> None: cls.state = Test1.create_state() @classmethod def tearDownClass(cls) -> None: cls.state = None def test_it(self): assert self.state is not None
Issue Analytics
- State:
- Created 3 years ago
- Reactions:2
- Comments:6 (2 by maintainers)
In order to maintain
unittest
compatibility, we should ensure that we run the setup and teardown methods on imported TestCase subclasses.With that in mind I’m closing this issue due to overlap with #7094. Thanks @pbatko for an excellent issue writeup, and to @harishkrao for your investigation - they were very helpful!
@symonk It wasn’t a regression for me. Btw, how would you approach bisecting and finding that out? I am thinking of installing a number of different versions of pytest using pip a testing them out on the minimal.
@harishkrao Sure, go ahead.