← Python/Testen
Les 3 van 8
MODULE 10 · 03◷ 30 min

Echte testfuncties met pytest uitvoeren

Na deze les kun je
  • Losse testfuncties door pytest laten vinden.
  • Met assert echte uitkomsten van een module vergelijken.
  • Een geslaagde test, mislukte test en ontbrekende test onderscheiden.

Een extra testklasse kan handig zijn, maar voor een kleine functie wil je soms direct één controle schrijven. Je wilt de gewone Python-assertie behouden en toch automatisch zien welke tests zijn uitgevoerd. Daarvoor gebruik je nu pytest.

Gewone functies, een echte testuitvoerder

pytest is een aparte testbibliotheek. Een bestand zoals test_names.py bevat gewone functies waarvan de naam met test_ begint. Anders dan bij unittest hoef je hiervoor geen klasse te maken of van TestCase te erven.

In een test schrijf je assert actual == expected. Bereken de verwachting zelf vanuit de afspraak en roep de echte functie aan voor de ontvangen waarde. Een vergelijking van de uitkomst met zichzelf kan geen verkeerde berekening ontdekken.

Het eerste voorbeeld heeft twee bestanden met controles en een klein startbestand. De startcode roept de echte pytest-uitvoerder aan en print gemeten aantallen. Bekijk zowel het aantal gevonden tests als de eindstatus.

Twee gewone testfuncties worden echt uitgevoerd
test_names.py
def clean_name(text):
    return text.strip().title()

def test_normal_name():
    assert clean_name("  sam ") == "Sam"

def test_empty_name():
    assert clean_name("   ") == ""
run_tests.py
import contextlib, io, pytest

class Recorded:
    def __init__(self):
        self.count = 0

    def pytest_collection_modifyitems(self, items):
        self.count = len(items)

recorded = Recorded()
with contextlib.redirect_stdout(io.StringIO()), contextlib.redirect_stderr(io.StringIO()):
    status = pytest.main(["-q", "--capture=sys", "-p", "no:cacheprovider", "test_names.py"], plugins=[recorded])
print("gevonden:", recorded.count)
print("status:", int(status))
Verwachte uitvoer
gevonden: 2
status: 0

Een test selecteren en het resultaat lezen

Testontdekking betekent dat pytest geschikte bestanden en functies zoekt. De gebruikelijke bestandsnamen zijn test_*.py en *_test.py. Een duidelijke naam zoals test_empty_name vertelt welke afspraak wordt gecontroleerd.

In een eigen terminal kun je python -m pytest test_names.py gebruiken. De selectie test_names.py::test_empty_name kiest één functie uit dat bestand. Zo onderzoek je eerst één mislukte afspraak, voordat je de hele verzameling opnieuw draait.

Een exitstatus vat de uitvoering samen. Status nul betekent dat deze uitvoering slaagde; status één hoort bij mislukte tests. Wanneer pytest geen tests vindt, geeft het status vijf: afwezigheid van fouten betekent dan nog geen bewijs.

De voorbeelden gebruiken een kleine rapporteur die echte pytest-resultaten telt. Hun getoonde aantallen zijn geen vooraf geschreven succesregels. De werkplek verzorgt de start; jij oefent in deze les met de inhoud van de testfuncties.

Veelgemaakte fout: een controle alleen benoemen

Een functie check_name begint niet met test_. In het tweede voorbeeld staat een onjuiste bewering in zo'n functie, maar pytest voert haar niet uit. De gemeten nul en status vijf maken het probleem zichtbaar.

Een verkeerd benoemde controle wordt niet uitgevoerd
test_names.py
def check_name():
    assert "sam" == "Sam"
run_tests.py
import contextlib, io, pytest

class Recorded:
    def __init__(self):
        self.count = 0

    def pytest_collection_modifyitems(self, items):
        self.count = len(items)

recorded = Recorded()
with contextlib.redirect_stdout(io.StringIO()), contextlib.redirect_stderr(io.StringIO()):
    status = pytest.main(["-q", "--capture=sys", "-p", "no:cacheprovider", "test_names.py"], plugins=[recorded])
print("gevonden:", recorded.count)
print("status:", int(status))
Verwachte uitvoer
gevonden: 0
status: 5

Verander daarom bij een mislukking eerst de juiste oorzaak: ontbreekt de test, klopt de verwachting niet of is de bibliotheek fout? Een test overslaan maakt die afspraak ook niet bewezen. Hetzelfde geldt voor return actual == expected: een waarde teruggeven controleert de afspraak niet.

Labels voor een leesbaar adres

De module labels maakt een label uit een lijst woorden. slug verwijdert buitenste spaties, maakt letters klein, laat lege onderdelen weg en voegt de overgebleven woorden met een koppelteken samen. Als niets overblijft, is het resultaat een lege string.

Schrijf vier afzonderlijke tests in test_labels.py. De module en de proefhelper staan vast, zodat jij de controles oefent. De les draait je tests tegen twee correcte uitvoeringen en gerichte fouten; een test moet de goede code accepteren en de betreffende fout vinden.

AAN DE SLAG 0/4
1

Eén woord normaliseren

Schrijf test_single: controleer dat labels.slug([" Blue "]) precies "blue" oplevert.

2

Een lege lijst controleren

Schrijf test_empty: controleer dat labels.slug([]) precies een lege string oplevert.

3

Een koppelteken tussen woorden

Schrijf test_separator: controleer dat labels.slug(["Go", "Py"]) precies "go-py" oplevert.

4

Lege onderdelen weglaten

Schrijf test_blank_parts: controleer dat labels.slug(["", " Rust ", " "]) precies "rust" oplevert.

Onthouden

  • Pytest vindt testbestanden en functies volgens een naamafspraak.
  • Vergelijk de echte uitkomst met een onafhankelijk berekende verwachting.
  • Kijk naar wat is uitgevoerd én naar de status; nul tests is geen geslaagde controle.

Hierna groepeer je verschillende invoervarianten bij één afspraak met parametrisatie.

test_labels.py
UitvoerPython
›
›_

Hier komt je resultaat.

Voer je code uit en ontdek
wat je hebt gemaakt.

0/4 stappen
EVEN MEEKIJKEN

Bekijk hoe het kan.

Je mag de oplossing bekijken. Deze poging telt dan niet mee als afgeronde les. Met Reset kun je daarna opnieuw zelf oefenen.