Software Testing & QA

Unit Testing with pytest

35 Min.

Setup

pip install pytest

pytest finds files named test_*.py and functions named test_*.

A first test

app/services/pricing.py:

from decimal import Decimal, ROUND_HALF_UP

ZERO_DECIMAL = {"jpy", "krw"}

def to_minor_units(amount, currency):
    value = Decimal(str(amount))
    if currency.lower() in ZERO_DECIMAL:
        return int(value.quantize(Decimal("1"), rounding=ROUND_HALF_UP))
    return int((value * 100).quantize(Decimal("1"), rounding=ROUND_HALF_UP))

tests/test_pricing.py:

from app.services.pricing import to_minor_units

def test_usd_rounds_instead_of_truncating():
    assert to_minor_units(19.99, "usd") == 1999

def test_jpy_has_no_minor_units():
    assert to_minor_units(100, "JPY") == 100
pytest -q
pytest tests/test_pricing.py -k jpy -v   # run matching tests, verbose
pytest -x                                # stop at first failure
pytest --lf                              # rerun only last failures

pytest uses plain assert and shows exactly which values differed when it fails.

Arrange, Act, Assert

def test_completing_a_module_awards_points_once():
    # Arrange
    user = make_user(points=0)
    # Act
    complete_module(user, module_id=7)
    complete_module(user, module_id=7)
    # Assert
    assert user.points == 10

Three clear sections make tests easy to read and debug.

Many cases with parametrize

import pytest

@pytest.mark.parametrize("amount, currency, expected", [
    (0.01, "usd", 1),
    (10, "eur", 1000),
    (19.995, "usd", 2000),
    (500, "jpy", 500),
])
def test_to_minor_units(amount, currency, expected):
    assert to_minor_units(amount, currency) == expected

Each row runs as its own test with its own result.

Testing errors

def test_rejects_negative_amounts():
    with pytest.raises(ValueError, match="negative"):
        charge(-5)

Test edge cases deliberately

For any function, ask: empty input? None? zero? negative? very large? unicode? duplicates? boundaries (exactly at a limit, one above, one below)? Most bugs live at the edges.

Exercise

Write parametrized tests for a slugify(text) function covering normal titles, punctuation, unicode, empty strings and maximum length.

Ihr Fortschritt wird nur in diesem Browser gespeichert.

Zur Prüfung einreichen (10 Punkte)