237

I am using selenium for end to end testing and I can't get how to use setup_class and teardown_class methods.

I need to set up browser in setup_class method, then perform a bunch of tests defined as class methods and finally quit browser in teardown_class method.

But logically it seems like a bad solution, because in fact my tests will not work with class, but with object. I pass self param inside every test method, so I can access objects' vars:

class TestClass:
  
    def setup_class(cls):
        pass
        
    def test_buttons(self, data):
        # self.$attribute can be used, but not cls.$attribute?  
        pass
        
    def test_buttons2(self, data):
        # self.$attribute can be used, but not cls.$attribute?
        pass
        
    def teardown_class(cls):
        pass
    

And it even seems not to be correct to create browser instance for class.. It should be created for every object separately, right?

So, I need to use __init__ and __del__ methods instead of setup_class and teardown_class?

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
avasin
  • 9,186
  • 18
  • 80
  • 127

8 Answers8

224

According to Fixture finalization / executing teardown code, the current best practice for setup and teardown is to use yield instead of return:

import pytest

@pytest.fixture()
def resource():
    print("setup")
    yield "resource"
    print("teardown")

class TestResource:
    def test_that_depends_on_resource(self, resource):
        print("testing {}".format(resource))

Running it results in

$ py.test --capture=no pytest_yield.py
=== test session starts ===
platform darwin -- Python 2.7.10, pytest-3.0.2, py-1.4.31, pluggy-0.3.1
collected 1 items

pytest_yield.py setup
testing resource
.teardown


=== 1 passed in 0.01 seconds ===

Another way to write teardown code is by accepting a request-context object into your fixture function and calling its request.addfinalizer method with a function that performs the teardown one or multiple times:

import pytest

@pytest.fixture()
def resource(request):
    print("setup")

    def teardown():
        print("teardown")
    request.addfinalizer(teardown)
    
    return "resource"

class TestResource:
    def test_that_depends_on_resource(self, resource):
        print("testing {}".format(resource))
ssc
  • 9,528
  • 10
  • 64
  • 94
Everett Toews
  • 10,337
  • 10
  • 44
  • 45
  • 3
    So you copy this into every test file that you're going to need the resource? – Andy Hayden Nov 11 '17 at 02:00
  • @AndyHayden Depending on how you write your fixtures, you could put it into every test file where you need it or you could put it in a conftest.py file https://stackoverflow.com/questions/34466027/in-py-test-what-is-the-use-of-conftest-py-files – Everett Toews Nov 13 '17 at 15:29
  • 10
    This however is not a class setup, right? It would execute before every test method in the class. – malhar Nov 15 '17 at 17:00
  • 1
    In this particular case, it's only executed when used as a param in a test method. e.g. the `resource` param in `test_that_depends_on_resource(self, resource)` – Everett Toews Nov 16 '17 at 17:34
  • 4
    Note that you can set the fixture scope to 'class' and autouse to true to make sure the code is called once for each class without having to include it as a parameter in any test call: ``` pytest.fixture(scope="class", autouse=True) def resource(): print("setup") yield "resource" print("teardown") ``` – Chris Oct 01 '20 at 12:14
  • This answer still leaves me with two questions. First: What if we don't have any "resource" to act on, but simply want arbitrary setup and teardown code to be executed? Do we write the fixture to yield nothing? Do we still have to call the generator somewhere in the test function? What happens if we just list it as a parameter and then never use it? Second: At what point in the test function are setup and teardown called? Do they happen before/after the `print("testing {}".format(resource))` line or before/after the function itself? – Alex Jansen Mar 25 '21 at 03:01
  • 1
    Update of the link: https://docs.pytest.org/en/stable/fixture.html#teardown-cleanup-aka-fixture-finalization – Sylvain Apr 19 '21 at 11:00
  • I think the question were not about fixuture but about setup and teardown ? – SylwekFr Apr 01 '22 at 09:12
  • scope of fixture is not honoured when running pytests in parallel with pytest-xdist. It ends up calling fixture for each method even when we specify scope=session – Vivek Dani Mar 08 '23 at 09:43
109

When you write "tests defined as class methods", do you really mean class methods (methods which receive its class as first parameter) or just regular methods (methods which receive an instance as first parameter)?

Since your example uses self for the test methods I'm assuming the latter, so you just need to use setup_method instead:

class Test:

    def setup_method(self, test_method):
        # configure self.attribute

    def teardown_method(self, test_method):
        # tear down self.attribute

    def test_buttons(self):
        # use self.attribute for test

The test method instance is passed to setup_method and teardown_method, but can be ignored if your setup/teardown code doesn't need to know the testing context. More information can be found here.

I also recommend that you familiarize yourself with py.test's fixtures, as they are a more powerful concept.

Samuel Harmer
  • 4,264
  • 5
  • 33
  • 67
Bruno Oliveira
  • 13,694
  • 5
  • 43
  • 41
  • 2
    Fixtures are weaker than class methods: they don't allow destruction of objects not created by them (which is often what's really necessary). Other than that, thank you for information. – wvxvw Nov 25 '18 at 12:14
  • This hit me while upgrading a codebase from a 3.0.x release of pytest up to a 4.x variant. Some older code used `setup_class` with mocked methods and the like that needed to be modernized. `setup_class(self, foo, bar)` --> `setup_method(self,function,foo,bar)` – jxramos Feb 24 '20 at 18:03
81

This might help http://docs.pytest.org/en/latest/xunit_setup.html

In my test suite, I group my test cases into classes. For the setup and teardown I need for all the test cases in that class, I use the setup_class(cls) and teardown_class(cls) classmethods.

And for the setup and teardown I need for each of the test case, I use the setup_method(method) and teardown_method(methods)

Example:

lh = <got log handler from logger module>

class TestClass:
    @classmethod
    def setup_class(cls):
        lh.info("starting class: {} execution".format(cls.__name__))

    @classmethod
    def teardown_class(cls):
        lh.info("starting class: {} execution".format(cls.__name__))

    def setup_method(self, method):
        lh.info("starting execution of tc: {}".format(method.__name__))

    def teardown_method(self, method):
        lh.info("starting execution of tc: {}".format(method.__name__))

    def test_tc1(self):
        <tc_content>
        assert 

    def test_tc2(self):
        <tc_content>
        assert

Now when I run my tests, when the TestClass execution is starting, it logs the details for when it is beginning execution, when it is ending execution and same for the methods..

You can add up other setup and teardown steps you might have in the respective locations.

Hope it helps!

lps
  • 1,403
  • 16
  • 28
Kiran Vemuri
  • 2,762
  • 2
  • 24
  • 40
  • Hi @Kiran, what is the difference between the `setup_class` vs `setup_method`? – Mr. Unnormalized Posterior Sep 10 '19 at 13:02
  • 3
    @imsrgadich When you organize your test cases into classes, _class is used for the setup and teardown steps of the class and _method are the respective steps for each test case method. – Kiran Vemuri Sep 12 '19 at 05:04
  • 2
    Damn... now I get it! was stuck on it for few hours. So, to put things in perspective. The `_class` for the whole class. Here, can be things like setting link to DB or loading the datafile. And then, each test case can have their own setup in the form of `_method`. Things are much clear now. Thanks a lot! – Mr. Unnormalized Posterior Sep 12 '19 at 11:07
30

As @Bruno suggested, using pytest fixtures is another solution that is accessible for both test classes or even just simple test functions. Here's an example testing python2.7 functions:

import pytest

@pytest.fixture(scope='function')
def some_resource(request):
    stuff_i_setup = ["I setup"]

    def some_teardown():
        stuff_i_setup[0] += " ... but now I'm torn down..."
        print stuff_i_setup[0]
    request.addfinalizer(some_teardown)

    return stuff_i_setup[0]

def test_1_that_needs_resource(some_resource):
    print some_resource + "... and now I'm testing things..."

So, running test_1... produces:

I setup... and now I'm testing things...
I setup ... but now I'm torn down...

Notice that stuff_i_setup is referenced in the fixture, allowing that object to be setup and torn down for the test it's interacting with. You can imagine this could be useful for a persistent object, such as a hypothetical database or some connection, that must be cleared before each test runs to keep them isolated.

ecoe
  • 4,994
  • 7
  • 54
  • 72
23

Your code should work just as you expect it to if you add @classmethod decorators.

@classmethod 
def setup_class(cls):
    "Runs once per class"

@classmethod 
def teardown_class(cls):
    "Runs at end of class"

See http://pythontesting.net/framework/pytest/pytest-xunit-style-fixtures/

Okken
  • 2,536
  • 21
  • 15
  • 5
    This is pretty much exactly what appears in the documentation. The trouble I had with the doc was that I had difficulty understanding the context: self is traditionally referred to as self, not cls, so this seemed weird to me, out of context of the class itself. Kiran (above) provides this context. – Cognitiaclaeves Apr 17 '19 at 16:41
  • 4
    @Cognitiaclaeves _"self is traditionally referred to as self, not cls"_ Yes, `self` is used for instance methods, where the first argument is the specific object instance on which the method operation is taking place, while `cls` is used for `@classmethod`s, which are bound to the class and not an instance of the class (i.e. an object). – code_dredd Aug 06 '19 at 21:16
  • The link http://pythontesting.net/framework/pytest/pytest-xunit-style-fixtures/ is 404. – Michael Tiemann Jul 18 '23 at 10:36
7
import pytest
class Test:
    @pytest.fixture()
    def setUp(self):
        print("setup")
        yield "resource"
        print("teardown")

    def test_that_depends_on_resource(self, setUp):
        print("testing {}".format(setUp))

In order to run:

pytest nam_of_the_module.py -v 
Kiran Sk
  • 873
  • 10
  • 24
  • What if there is no resource to yield? Say, I need to run a command line utility (a server application) at the start of all the tests, and then tear down that utlity after the test? – Neil Aug 28 '22 at 03:54
5

If you are like me, that want's to have quick recipes off all possible variation in once for later use or just a refresher, here you have it.

So, there are 2 main ways in Pytest to setup/teardown test (without including unittest intgration, that is, when you actually using unittest)

1) Fixture (the pytest way )

2) xunit-style (similar to unittest)

These recipes can be found in my Programming-CookBook here -> PyTest Recipes

Fixture

Collection of fixture setup/teardown


import pytest

counter = 0

def add_counter():
    global counter
    counter += 1

# ---------------------------
# yield fixtures (recommended)
# ---------------------------


@pytest.fixture
def get_counter():
    print(f"{counter}) -- Fixture (yield)")
    add_counter()
    yield counter


def test_count_is_1(get_counter):
    assert get_counter == 1

def test_count_is_2(get_counter):
    assert get_counter == 2

# ---------------------------
# Adding finalizers directly
# ---------------------------

@pytest.fixture
def get_counter_direct(request):
    print(f"{counter}) -- Fixture (request)")
    add_counter()

    request.addfinalizer(add_counter)
    return counter

def test_count_is_3(get_counter_direct):
    assert get_counter_direct == 3

which results in

================================================================================================================ PASSES ================================================================================================================
___________________________________________________________________________________________________________ test_count_is_1 ____________________________________________________________________________________________________________
-------------------------------------------------------------------------------------------------------- Captured stdout setup ---------------------------------------------------------------------------------------------------------
0) -- Fixture (yield)
___________________________________________________________________________________________________________ test_count_is_2 ____________________________________________________________________________________________________________
-------------------------------------------------------------------------------------------------------- Captured stdout setup ---------------------------------------------------------------------------------------------------------
1) -- Fixture (yield)
___________________________________________________________________________________________________________ test_count_is_3 ____________________________________________________________________________________________________________
-------------------------------------------------------------------------------------------------------- Captured stdout setup ---------------------------------------------------------------------------------------------------------
2) -- Fixture (request)
========================================================================================================== 3 passed in 0.01s ==========================================================================================================

xunit-style

These are all collection of xunit-style like setup/teardown ways. Here I added a counter so that the order is visible

import pytest

counter = 0

def add_counter():
    global counter
    counter += 1

# ---------------------------
# Module Level Setup/TearDown
# ---------------------------
def setup_module(module):
    """setup any state specific to the execution of the given module."""
    add_counter()
    print(f"{counter}) -- SetUp (module)")

def teardown_module(module):
    """teardown any state that was previously setup with a setup_module method."""
    add_counter()
    print(f"{counter}) -- tearDown (module)")

class TestSomeClass:

    # ---------------------------
    # Class level setup/teardown
    # ---------------------------
    @classmethod
    def setup_class(cls):
        """setup any state specific to the execution of the given class (which usually contains tests)."""
        add_counter()
        print(f"{counter}) -- SetUp (class)")

    @classmethod
    def teardown_class(cls):
        """teardown any state that was previously setup with a call to setup_class. """
        add_counter()
        print(f"{counter}) -- tearDown (class)")

    # ---------------------------
    # Class level setup/teardown
    # ---------------------------
    def setup_method(self, method):
        """setup any state tied to the execution of the given method in a class.  setup_method is invoked for every test method of a class."""
        add_counter()
        print(f"{counter}) -- SetUp (method)")

    def teardown_method(self, method):
        """teardown any state that was previously setup with a setup_method call. """
        add_counter()
        print(f"{counter}) -- tearDown (method)")

    def test_is_1_a_number(self):
        assert (1).__class__ is int


# ---------------------------
# module level  setup/teardown
# ---------------------------
def setup_function(function):
    """setup any state tied to the execution of the given function. Invoked for every test function in the module."""
    add_counter()
    print(f"{counter}) -- SetUp (function)")


def teardown_function(function):
    """teardown any state that was previously setup with a setup_function call."""
    add_counter()
    print(f"{counter}) -- tearDown (function)")

def test_something_at_module_level():
    assert (1).__class__ is int

which results in

================================================================================================================ PASSES ================================================================================================================
___________________________________________________________________________________________________ TestSomeClass.test_is_1_a_number ___________________________________________________________________________________________________
-------------------------------------------------------------------------------------------------------- Captured stdout setup ---------------------------------------------------------------------------------------------------------
1) -- SetUp (module)
2) -- SetUp (class)
3) -- SetUp (method)
------------------------------------------------------------------------------------------------------- Captured stdout teardown -------------------------------------------------------------------------------------------------------
4) -- tearDown (method)
5) -- tearDown (class)
____________________________________________________________________________________________________ test_something_at_module_level ____________________________________________________________________________________________________
-------------------------------------------------------------------------------------------------------- Captured stdout setup ---------------------------------------------------------------------------------------------------------
6) -- SetUp (function)
------------------------------------------------------------------------------------------------------- Captured stdout teardown -------------------------------------------------------------------------------------------------------
7) -- tearDown (function)
8) -- tearDown (module)
========================================================================================================== 2 passed in 0.01s ===========================================================================================================

Documentaiton

Federico Baù
  • 6,013
  • 5
  • 30
  • 38
1

I'm not sure I got the specifics of using Selenium in your original questions, but in case you were simply asking about how to use a more classical setUp/tearDown style, Pytest supports most unittest features, so you could do something like:

import unittest


class TestHello(unittest.TestCase):

    def setUp(self):
        print('running setUp')

    def test_one(self):
        print('running test_one')

    def test_two(self):
        print('running test_two')

    def tearDown(self):
        print('running tearDown')

Which produces:

$ pytest -s -v
====================== test session starts =======================
platform linux -- Python 3.8.2, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- /gnu/store/nckjv3ccwdi6096j478gvns43ssbls2p-python-wrapper-3.8.2/bin/python
cachedir: .pytest_cache
hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/tmp/test/.hypothesis/examples')
rootdir: /tmp/test
plugins: hypothesis-5.4.1
collected 2 items                                                

test_hw.py::TestHello::test_one running setUp
running test_one
running tearDown
PASSED
test_hw.py::TestHello::test_two running setUp
running test_two
running tearDown
PASSED
Apteryx
  • 5,822
  • 3
  • 16
  • 18
  • I think it should be `setup` instead of `setUp`, at least for pytest-7.1.2. – schade96 Jul 09 '22 at 21:37
  • @schade96 are you sure? I don't see such a change in the changelog (https://docs.pytest.org/en/7.1.x/changelog.html), and at least one bug fix refers to `tearDown` in 7.1. – Apteryx Oct 12 '22 at 13:59