question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

pytest runs imported unittest.TestCase for the second time after it had already called testDownClass

See original GitHub issue

a 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:

  1. Never execute test classes that were imported from another module. Execute only those test classes that are defined in the current module.

  2. Execute setUpClass and testDownClass (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 the setUpClass and testDownClass 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:

  1. Create a directory with the 2 test files (attached below).
  2. cd to that directory and run pytest (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

  1. 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"
    
    
  2. 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:closed
  • Created 3 years ago
  • Reactions:2
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

2reactions
Zac-HDcommented, May 11, 2020

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!

1reaction
pbatkocommented, Apr 23, 2020

@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.

Read more comments on GitHub >

github_iconTop Results From Across the Web

unittest — Unit testing framework — Python 3.11.1 ...
Test discovery loads tests by importing them. Once test discovery has found all the test files from the start directory you specify it...
Read more >
How to use unittest-based tests with pytest
To run an existing unittest -style test suite using pytest , type: pytest tests. pytest will automatically collect unittest.TestCase subclasses and their ...
Read more >
Tests being run from imported class - python - Stack Overflow
This results in TestA tests being run twice: one when its module is found and scanned, and another time when TestB module is...
Read more >
A Guide to Python Unit Testing with unittest and pytest
Learn what software testing is, and how to run Python unit tests with unittest and pytest, two key frameworks for Python unit testing....
Read more >
How to Write Unit Tests in Python, Part 3: Web Applications
Run pytest as above one more time to also include this new test case. When I run it here, my coverage increased to...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found