As already mentioned in a previous article, pytest is a powerful testing framework for Python that offers a wide range of features, including parametrization. Parametrization allows you to run the same test with different sets of input data, making it easier to test multiple scenarios without duplicating code.
Parameterizing tests
pytest provides the @pytest.mark.parametrize decorator to parameterize tests. This decorator takes two arguments: the name of the parameter(s) and a list of input data. Here’s a simple example:
import pytest
@pytest.mark.parametrize("num1, num2, expected", [
(2, 3, 5),
(4, 5, 9),
(6, 7, 13),
])
def test_add(num1, num2, expected):
assert num1 + num2 == expected
In this example, the test_add function is parameterized with three arguments: num1, num2, and expected. The decorator specifies the input data as a list of tuples, where each tuple represents one set of test inputs.
Parameterizing tests via a test data file
Another option for generating inputs is using a file as a config or manifest. In this example we use YAML.
import pytest
import yaml
# Load test data from YAML file
with open("test_data.yaml", "r") as file:
test_data = yaml.safe_load(file)
# Parametrized test for making HTTP requests
@pytest.mark.parametrize("payload", test_data["user_payloads"])
def test_post_user(payload):
import requests
url = "http://api.example.com/users"
response = requests.post(url, json=payload)
assert response.status_code == 201
assert response.json()["name"] == payload["name"]
assert response.json()["age"] == payload["age"]
# Parametrized test for database operations
@pytest.mark.parametrize("user", test_data["users"])
def test_insert_user(user):
import database
db = database.connect()
cursor = db.cursor()
query = "INSERT INTO users (name, age) VALUES (%s, %s)"
cursor.execute(query, (user["name"], user["age"]))
db.commit()
cursor.close()
db.close()
# Add assertions to verify the insertion
# Parametrized test for file operations
@pytest.mark.parametrize("file_data", test_data["file_contents"])
def test_write_file(file_data, tmp_path):
file_path = tmp_path / "test_file.txt"
with open(file_path, "w") as file:
file.write(file_data["content"])
# Add assertions to verify the file content
We load the test data from a YAML file named test_data.yaml. The YAML file could look like this:
user_payloads:
- name: Alice
age: 25
- name: Bob
age: 30
- name: Charlie
age: 35
users:
- name: David
age: 40
- name: Eve
age: 45
file_contents:
- content: "This is a test file."
- content: "Another test file content."
We then define three parametrized tests:
test_post_user- parameterized withuser_payloadsfrom the YAML file. It makes HTTP POST requests to/userswith the specified payloads and verifies the response.test_insert_user- parameterized withusersfrom the YAML file. It connects to a database and inserts the user data.test_write_file- parameterized withfile_contentsfrom the YAML file. It creates temporary files and writes the specified content to them.
By using parametrization and reading test data from a YAML file, you can easily manage and maintain test cases for different scenarios. The YAML file provides a centralized location for storing test data, making it easier to modify or add new test cases without modifying the test code itself.
