Eén woord normaliseren
Schrijf test_single: controleer dat labels.slug([" Blue "]) precies "blue" oplevert.
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.
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.
def clean_name(text):
return text.strip().title()
def test_normal_name():
assert clean_name(" sam ") == "Sam"
def test_empty_name():
assert clean_name(" ") == ""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))gevonden: 2 status: 0
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.
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.
def check_name():
assert "sam" == "Sam"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))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.
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.
Schrijf test_single: controleer dat labels.slug([" Blue "]) precies "blue" oplevert.
Schrijf test_empty: controleer dat labels.slug([]) precies een lege string oplevert.
Schrijf test_separator: controleer dat labels.slug(["Go", "Py"]) precies "go-py" oplevert.
Schrijf test_blank_parts: controleer dat labels.slug(["", " Rust ", " "]) precies "rust" oplevert.
Hierna groepeer je verschillende invoervarianten bij één afspraak met parametrisatie.
Voer je code uit en ontdek
wat je hebt gemaakt.
Je hoeft het niet in één keer goed te doen. Na het uitvoeren zie je hier welke stappen al kloppen.