Python · Testing with pytest · Beginner
Parametrized Tests
Run one test function against multiple inputs with @pytest.mark.parametrize.
Quick topic start and explanations before exercises (exercises below):
Parametrize Patterns
#Parametrize Quick Reference
#Exercises:
Parametrize square(n)
#Write `square(n)` that returns `n * n`. Use `@pytest.mark.parametrize` to test it with at least 5 different inputs covering: a positive integer, a negative integer, zero, a float, and a large number.
import pytest
def square(n):
pass
@pytest.mark.parametrize("n, expected", [
# add 5 rows
])
def test_square(n, expected):
assert square(n) == expected
Solution
import pytest
def square(n):
return n * n
@pytest.mark.parametrize("n, expected", [
(3, 9),
(-4, 16),
(0, 0),
(0.5, 0.25),
(100, 10000),
])
def test_square(n, expected):
assert square(n) == expected
Parametrize is_palindrome
#Write `is_palindrome(s)` that returns True if a string reads the same backwards. Use `@pytest.mark.parametrize` with at least 4 palindromes and 3 non-palindromes, including the expected boolean as a second parameter.
import pytest
def is_palindrome(s):
pass
@pytest.mark.parametrize("s, expected", [
# palindromes (True) and non-palindromes (False)
])
def test_is_palindrome(s, expected):
assert is_palindrome(s) == expected
Solution
import pytest
def is_palindrome(s):
return s == s[::-1]
@pytest.mark.parametrize("s, expected", [
("racecar", True),
("madam", True),
("level", True),
("", True),
("hello", False),
("python", False),
("abcba", True),
("abcde", False),
])
def test_is_palindrome(s, expected):
assert is_palindrome(s) == expected
Parametrize with Two Arguments
#Use `@pytest.mark.parametrize` to test `clamp(value, lo, hi)` with at least 5 cases covering: value within range, at lower boundary, at upper boundary, below lower bound, and above upper bound. Use four parameters per row: `value`, `lo`, `hi`, and `expected`.
import pytest
def clamp(value, lo, hi):
return max(lo, min(value, hi))
@pytest.mark.parametrize("value, lo, hi, expected", [
# add rows here
])
def test_clamp(value, lo, hi, expected):
assert clamp(value, lo, hi) == expected
Solution
import pytest
def clamp(value, lo, hi):
return max(lo, min(value, hi))
@pytest.mark.parametrize("value, lo, hi, expected", [
(5, 0, 10, 5),
(0, 0, 10, 0),
(10, 0, 10, 10),
(-5, 0, 10, 0),
(15, 0, 10, 10),
(-1, 0, 10, 0),
])
def test_clamp(value, lo, hi, expected):
assert clamp(value, lo, hi) == expected
Add Custom IDs
#The parametrized test below auto-generates unhelpful IDs like `test_parse_int[42-42]`. Add `ids=[...]` to the decorator with descriptive names. Run `pytest -v` before and after to see the difference.
import pytest
def parse_int(s):
return int(s.strip())
@pytest.mark.parametrize("s, expected", [
("42", 42),
(" 7 ", 7),
("-10", -10),
("0", 0),
])
def test_parse_int(s, expected):
assert parse_int(s) == expected
Solution
import pytest
def parse_int(s):
return int(s.strip())
@pytest.mark.parametrize("s, expected", [
("42", 42),
(" 7 ", 7),
("-10", -10),
("0", 0),
], ids=["plain-integer", "with-whitespace", "negative", "zero"])
def test_parse_int(s, expected):
assert parse_int(s) == expected
Parametrize Edge Cases for a Validator
#Write `is_valid_username(s)` that returns True only if the string is 3–20 characters, contains only letters and digits, and does not start with a digit. Use `@pytest.mark.parametrize` to test at least 3 valid and 4 invalid usernames.
import pytest
def is_valid_username(s):
pass # implement the three validation rules
@pytest.mark.parametrize("username, expected", [
# valid (True) and invalid (False) cases
])
def test_is_valid_username(username, expected):
assert is_valid_username(username) == expected
Solution
import pytest
def is_valid_username(s):
if not s or not (3 <= len(s) <= 20):
return False
if s[0].isdigit():
return False
return all(c.isalnum() for c in s)
@pytest.mark.parametrize("username, expected", [
("alice", True),
("user123", True),
("A1b2C3", True),
("ab", False),
("a" * 21, False),
("1alice", False),
("alice!", False),
("", False),
])
def test_is_valid_username(username, expected):
assert is_valid_username(username) == expected