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?
7 Answers
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)) 8 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 testThe 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.
2This might help
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> assertNow 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!
3As @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.
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
2import 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 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