← Python/Testen
Les 2 van 8
MODULE 10 · 02◷ 30 min

Meerdere controles met unittest organiseren

Na deze les kun je
  • Testmethoden in een unittest.TestCase verzamelen.
  • Met assertEqual een concrete uitkomst controleren.
  • Gevonden tests, mislukte controles en uitvoerfouten onderscheiden.

Je eerste test ontdekt een fout, maar je programma heeft inmiddels meer afspraken. Als je iedere functie handmatig onderaan het bestand aanroept, vergeet je gemakkelijk een nieuwe controle. Je wilt dat één uitvoerder alle bedoelde tests vindt en hun resultaten bij elkaar houdt.

Een vaste plaats voor je controles

unittest is de testbibliotheek die met Python wordt meegeleverd. Een klasse die erft van unittest.TestCase verzamelt je controles als methoden. Je kent klassen en overerving al; hier gebruik je een bestaande basis met hulpmethoden.

Een testmethode begint in deze les met test_ en krijgt self. Met self.assertEqual(actual, expected) vergelijk je de ontvangen waarde met de verwachte waarde. Je schrijft een vergelijking, geen opdracht om het antwoord te veranderen.

Het eerste voorbeeld test een labelmaker. De gewone invoer en de lege invoer hebben ieder een eigen methode, zodat je later afzonderlijk ziet welke afspraak is gebroken.

Twee echte unittest-methoden uitvoeren
import unittest


def label(text):
    return text.strip().upper()


class LabelTests(unittest.TestCase):
    def test_regular_label(self):
        self.assertEqual(label(" fiets "), "FIETS")

    def test_empty_label(self):
        self.assertEqual(label("   "), "")


suite = unittest.defaultTestLoader.loadTestsFromTestCase(LabelTests)
result = unittest.TestResult()
suite.run(result)
print("Uitgevoerd:", result.testsRun)
print("Mislukte controles:", len(result.failures))
print("Uitvoerfouten:", len(result.errors))
Verwachte uitvoer
Uitgevoerd: 2
Mislukte controles: 0
Uitvoerfouten: 0

Vinden is iets anders dan uitvoeren

Een testloader zoekt de testmethoden en maakt daar uitvoerbare tests van. loadTestsFromTestCase krijgt de klasse, niet een zelfgekozen antwoord of een lijst succesmeldingen. De suite die terugkomt groepeert de gevonden controles.

Daarna voert suite.run(result) die controles echt uit. Het resultaat telt hoeveel tests draaiden en verzamelt wat misging. Het voorbeeld print die gemeten aantallen; het zegt niet vooraf dat alles gelukt is.

Een verzameling in een lijst zetten voert haar tests nog niet uit. Dat lijkt op het verschil tussen een functie bewaren en haar aanroepen. Houd laden en draaien daarom als twee herkenbare stappen in je hoofd.

Eén test, één duidelijke afspraak

Een naam zoals test_empty_label vertelt sneller wat misging dan test_second. Kies één hoofdreden waarom een methode zou falen. Twee sterk verschillende afspraken passen vaak beter in twee methoden.

Een failure betekent dat een controle niet aan haar verwachting voldeed. Een error betekent dat de test door een andere onverwachte fout niet normaal kon worden afgerond, bijvoorbeeld doordat een naam ontbreekt. Beide vragen aandacht, maar ze wijzen je naar een ander begin van het onderzoek.

Verander bij een rood resultaat niet meteen de verwachting. Reken eerst de afspraak na en bekijk de ontvangen waarde. Anders kun je een fout in het programma per ongeluk tot de nieuwe norm maken.

Veelgemaakte fout: nul tests toch vertrouwen

Een methode check_label wordt met de standaardnaamgeving niet als test gevonden. Het tweede voorbeeld bevat precies die fout. Er mislukt niets, omdat er ook niets wordt uitgevoerd.

Een verkeerde methodenaam laat nul tests draaien
import unittest


class HiddenTests(unittest.TestCase):
    def check_label(self):
        self.assertEqual("fiets".upper(), "VERKEERD")


suite = unittest.defaultTestLoader.loadTestsFromTestCase(HiddenTests)
result = unittest.TestResult()
suite.run(result)
print("Uitgevoerd:", result.testsRun)
print("Geslaagd volgens het lege resultaat:", result.wasSuccessful())
Verwachte uitvoer
Uitgevoerd: 0
Geslaagd volgens het lege resultaat: True

Controleer daarom naast de uitkomst ook dat het bedoelde aantal tests draaide. Een leeg rapport bewijst niets over de labelmaker. De lescontrole accepteert evenmin een overgeslagen test als bewijs voor een afspraak.

Jouw voorraadquotum

De meegeleverde functie quota.remaining(limit, used) berekent het resterende aantal plaatsen. Zij geeft het verschil terug zolang er ruimte is en nul wanneer de grens bereikt of overschreden is. De argumenten zijn hier niet-negatieve gehele getallen.

Schrijf de gevraagde methoden in RemainingTests. Elke methode roept de echte functie aan en vergelijkt haar antwoord met de afgesproken waarde. De bibliotheek blijft meegeleverd, zodat jij je op de teststructuur kunt richten.

AAN DE SLAG 0/4
1

Een gevonden testmethode

Maak RemainingTests als subklasse van unittest.TestCase en schrijf test_available. Controleer met self.assertEqual dat quota.remaining(10, 3) zeven geeft.

2

Precies aan de grens

Schrijf de methode test_exact_limit. Controleer dat quota.remaining(10, 10) nul geeft.

3

Geen negatieve resterende ruimte

Schrijf test_over_limit. Controleer dat quota.remaining(10, 13) nul geeft en ontdek een berekening die min drie laat staan.

4

Meer dan één vast antwoord

Schrijf test_other_limits. Controleer in dezelfde methode zowel quota.remaining(5, 1) als quota.remaining(20, 2) met hun eigen verwachte waarde.

Onthoud dit

  • Een TestCase verzamelt herkenbaar genoemde testmethoden.
  • De loader vindt tests; een suite moet ze daarna werkelijk uitvoeren.
  • Controleer ook hoeveel tests draaiden en welk soort probleem werd gemeld.

Hierna schrijf je dezelfde soort afspraken als compacte pytest-functies.

test_quota.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.