B18 - pytest framework
source: pytest docs
invoking
pytest <filename or directory or "keyword expressions">
assertion
- an
assertstatement checks whether a given condition is true - if it does not pass, the test fails
## main.py
import pytest
def func():
return 5
def test_func():
assert func() == 4, "unmatched"
- the message alongside assertion is printed
expected errors
def test_zero_division():
with pytest.raises(ZeroDivisionError):
1 / 0
def test_recursion_depth():
with pytest.raises(RuntimeError) as excinfo:
def f():
f()
f()
assert "maximum recursion" in str(excinfo.value)
parameterised testing
- when a function needs to be tested multiple times with different parameters,
pytest.mark.parameterize()can be used
## mod.py
def square(x: int) -> int:
"""return the square of x."""
return x * x
## mod_test.py
import mod
import pytest
def test_square() -> None:
assert mod.square(2) == 4, "Square of 2 should be 4"
def test_square_negative() -> None:
assert mod.square(-2) == 4, "Square of -2 should be 4"
def test_square_zero() -> None:
assert mod.square(0) == 0, "Square of 0 should be 0"
def test_square_negative_zero() -> None:
assert mod.square(-0) == 0, "Square of -0 should be 0"
def test_square_float() -> None:
assert mod.square(2.5) == pytest.approx(6.25), "Square of 2.5 should be 6.25"
- since the tests have the same format, it can be summarized using
@pytest.mark.parametrize()
@pytest.mark.parametrize(
"input, expected", [(2, 4), (-2, 4), (0, 0), (-0, 0), (2.5, 6.25)]
)
def test_square(input: int, expected: int) -> None:
assert mod.square(input) == expected
fixtures
- a function decorated as a fixture will be ran every time a test is ran
- eg: initialising a new instance of an object for every test
import pytest
class C:
def f(self):
return 1
def g(self):
return 2
@pytest.fixture
def c_instance():
return C()
def test_f(c_instance):
assert c_instance.f() == 1
def test_g(c_instance):
assert c_instance.g() == 2