Exploring the Purpose of Pytest Fixtures: A Practical Guide
This blog explains how to use Pytest fixtures for initializing and cleaning up Selenium WebDriver, with a practical example using the Sauce Labs Demo website.
Join the DZone community and get the full member experience.
Join For FreeTo set the groundwork for this article, let's first understand what Pytest is.
Pytest is a popular testing framework for Python that simplifies the process of writing scalable and maintainable test cases. It supports fixtures, parameterized testing, and detailed test reporting, making it a powerful choice for both unit and functional testing. Pytest's simplicity and flexibility have made it a go-to framework for developers and testers alike.
How to Install Pytest
Pytest requires: Python 3.8+ or PyPy3.
1. Run the following command in your command line:
pip install -U pytest
OR
pip3 install -U pytest
You can refer to the image below for your reference:
Check that you have installed the correct version:
$ pytest --version
You can refer to the image below for your reference:
Purpose of Fixtures
Fixtures in Pytest provide a robust way to manage setup and teardown logic for tests. They are particularly useful for initializing resources, mocking external services, or performing setup steps that are shared across multiple tests. By using fixtures, you can avoid repetitive code and ensure a consistent testing environment.
Common Use Cases
- Initializing web drivers for browser interactions
- Navigating to a specific URL before running tests
- Clean up resources after tests are completed
Example of Using Pytest Fixtures in Selenium
Test Scenario
We want to verify the login functionality of the Sauce Labs Demo website. The steps include:
- Open a browser and navigate to the login page.
- Perform login operations.
- Verify successful login.
- Close the browser after the test.
Code Implementation
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
@pytest.fixture(scope="module")
def browser():
# Setup: Initialize the WebDriver
driver = webdriver.Chrome() # Replace with your WebDriver (Chrome, Firefox, etc.)
driver.get("https://www.saucedemo.com/") # Navigate to the login page
yield driver # Provide the WebDriver instance to the tests
# Teardown: Close the browser after tests
driver.quit()
def test_login_success(browser):
# Use the WebDriver instance provided by the fixture
browser.find_element(By.ID, "user-name").send_keys("standard_user")
browser.find_element(By.ID, "password").send_keys("secret_sauce")
browser.find_element(By.ID, "login-button").click()
# Verify successful login by checking the presence of a product page element
assert "Products" in browser.page_source
Explanation
Fixture Definition
@pytest.fixture(scope="module")
def browser():
driver = webdriver.Chrome()
driver.get("https://www.saucedemo.com/")
yield driver
driver.quit()
@pytest.fixture(scope="module")
: Defines a fixture namedbrowser
with a scope of "module," meaning it will be set up once per module and shared among all tests in that module- Setup: Initializes the WebDriver and navigates to the Sauce Labs Demo login page
- Yield: Provides the WebDriver instance to the test functions
- Teardown: Closes the browser after all tests are completed
Using the Fixture
def test_login_success(browser):
browser.find_element(By.ID, "user-name").send_keys("standard_user")
browser.find_element(By.ID, "password").send_keys("secret_sauce")
browser.find_element(By.ID, "login-button").click()
assert "Products" in browser.page_source
- The
test_login_success
function uses thebrowser
fixture. - It interacts with the login page by sending credentials and clicking the login button.
- Finally, it asserts the login's success by verifying the presence of the "Products" page element.
How to Use a Parameterized Fixture in Pytest
A parameterized fixture in Pytest allows you to run the same test function multiple times with different input values provided by the fixture. This is done by setting the params
argument in the @pytest.fixture
decorator.
Here’s an example of a parameterized fixture:
import pytest
# Define a parameterized fixture
@pytest.fixture(params=["chrome", "firefox", "safari"])
def browser(request):
# The 'request' object gives access to the parameter value
return request.param
# Use the parameterized fixture in a test
def test_browser_launch(browser):
print(f"Launching browser: {browser}")
# Simulate browser testing logic
assert browser in ["chrome", "firefox", "safari"]
Explanation
1. Definition
- The
@pytest.fixture
decorator uses theparams
argument to provide a list of values (["chrome", "firefox", "safari"]
). - For each value in the list, Pytest will call the fixture once and pass the current value to the test function.
2. Usage
- In the test
test_browser_launch
, the fixturebrowser
is passed as an argument. - The
request.param
in the fixture gives the current parameter value being used.
3. Test Execution
- Pytest will run the
test_browser_launch
test three times, once for each browser:browser = "chrome"
browser = "firefox"
browser = "safari"
Output
When you run the test:
pytest -s test_file.py
You’ll see:
Launching browser: chrome
Launching browser: firefox
Launching browser: safari
This approach is particularly useful for testing the same logic or feature across multiple configurations, inputs, or environments.
How to Pass Data Dynamically into a Fixture
In Python, you can use a fixture to pass data dynamically to your test cases with the help of the Pytest framework. A fixture in Pytest allows you to set up data or resources that can be shared across multiple test cases. You can create dynamic test data inside the fixture and pass it to your test functions by including it as a parameter in the test.
Here's how you can use a fixture to pass dynamic data to your test cases:
Example
import pytest
import time
# Define a fixture that generates dynamic data
@pytest.fixture
def dynamic_data():
# You can generate dynamic data based on various factors, such as timestamps or random values
return "Dynamic Data " + str(time.time())
# Use the fixture in your test case
def test_using_dynamic_data(dynamic_data):
print(dynamic_data) # This prints dynamically generated data
assert "Dynamic Data" in dynamic_data # Your test logic using dynamic data
Explanation
1. Fixture Creation
The @pytest.fixture
decorator is used to create a fixture. The fixture function (dynamic_data
) generates dynamic data (in this case, appending the current timestamp to the string).
2. Using Fixture in Test
In the test function (test_using_dynamic_data
), the fixture is passed as a parameter. Pytest automatically detects that the test function needs dynamic_data
, so it calls the fixture and provides the generated data to the test.
3. Dynamic Data
Each time the test runs, the fixture generates fresh, dynamic data (based on the current timestamp), making the test run with different data.
Benefits of Using Fixtures
- Code reusability. Define setup and teardown logic once and reuse it across multiple tests.
- Readability. Keep test logic focused on assertions rather than setup/teardown details.
- Consistency. Ensure each test starts with a clean state.
Closing Thoughts
Pytest fixtures are a powerful tool for managing test resources efficiently. Adopting fixtures lets you write clean, reusable, and maintainable test code. Try implementing them in your test automation projects, starting with simple examples like the one above.
Opinions expressed by DZone contributors are their own.
Comments