What is the correct way to set up and tear down resources for a pytest class when using Selenium for end-to-end testing?
I need to initialize the browser in the setup_class
method, perform several tests defined as class methods, and then quit the browser in the teardown_class
method. However, I’m struggling to understand how to properly use setup_class
and teardown_class
methods in this context.
It seems like a bad solution logically, because my tests will work with the instance, not the class. Since I pass the self
parameter inside every test method, I can access instance variables, but I can’t access cls
variables in the same way.
Here’s the structure of my current code:
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
This approach doesn’t feel right for creating the browser instance at the class level. Shouldn’t the browser instance be created for each object separately instead of for the whole class?
Would it be better to use __init__
and __del__
methods instead of setup_class
and teardown_class
for managing browser initialization and cleanup in pytest setup?
Instead of using setup_class
and teardown_class
, you can use setup_method
and teardown_method
to manage resources on an instance level. This means each test runs in complete isolation with its own browser instance, ensuring test independence and avoiding shared state issues.
import pytest
from selenium import webdriver
class TestClass:
def setup_method(self):
# Setup browser instance for each test
self.browser = webdriver.Chrome()
def test_buttons(self):
# Use self.browser here to interact with the browser
self.browser.get('http://example.com')
assert self.browser.title == 'Example Domain'
def test_buttons2(self):
# Another test using its own browser instance
self.browser.get('http://anotherexample.com')
assert self.browser.title == 'Another Example'
def teardown_method(self):
# Teardown browser instance after each test
if hasattr(self, 'browser'):
self.browser.quit()
Why this works?
- Each test runs in isolation, preventing state leakage.
- If one test fails or crashes, it doesn’t affect others.
- Ideal for independent, repeatable, and parallelized tests.
If opening a new browser for every test is too slow, you might want to set up the browser once per test class. This reduces overhead but comes with a risk: tests could interfere with each other.
import pytest
from selenium import webdriver
class TestClass:
@classmethod
def setup_class(cls):
# Setup browser instance once for the entire class
cls.browser = webdriver.Chrome()
def test_buttons(self):
# Use cls.browser here, shared across tests
self.browser.get('http://example.com')
assert self.browser.title == 'Example Domain'
def test_buttons2(self):
# Another test using the same browser instance
self.browser.get('http://anotherexample.com')
assert self.browser.title == 'Another Example'
@classmethod
def teardown_class(cls):
# Teardown browser instance once after all tests
if hasattr(cls, 'browser'):
cls.browser.quit()
Why this works?
- Faster since you don’t have to create and close a browser for every test.
- Useful when tests depend on a shared session (e.g., logging in once and running multiple tests).
Potential Issue:
- If one test modifies the browser state (e.g., navigating to a different page), it may affect the next test.
- Harder to parallelize tests.
Instead of relying on pytest’s built-in setup methods, you can manage resources within the test class using __init__
and __del__
.
import pytest
from selenium import webdriver
class TestClass:
def __init__(self):
# Initialize browser for each test instance
self.browser = webdriver.Chrome()
def test_buttons(self):
# Use self.browser here to interact with the browser
self.browser.get('http://example.com')
assert self.browser.title == 'Example Domain'
def test_buttons2(self):
# Another test using the same browser instance
self.browser.get('http://anotherexample.com')
assert self.browser.title == 'Another Example'
def __del__(self):
# Cleanup browser instance when the test object is deleted
if hasattr(self, 'browser'):
self.browser.quit()
Why this works?
- A clean, object-oriented approach to managing resources.
- The browser instance exists as long as the test object is alive.
- Automatic cleanup when the object is deleted.
Potential Issue:
__del__
is not always called immediately (Python’s garbage collection is unpredictable).
- Less explicit than pytest’s built-in setup/teardown methods.