B18 - pytest framework

source: pytest docs

invoking

pytest <filename or directory or "keyword expressions">

assertion

## main.py
import pytest

def func():
	return 5

def test_func():
	assert func() == 4, "unmatched"

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

## 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"
@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

fixture and teardown

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
@pytest.fixture
def resource():
	# setup
	data = {"connection": "open"}
	yield data  # test runs here
	
	# teardown
	data["connection"] = "closed"
	
def test_example(resource):
    assert resource["connection"] == "open"