Hoofdstuk 11 - Code Testen

01 - Een functie testen
def get_formatted_name(first, last):
    """Maak een net opgemaakte naam"""
    full_name = first + ' ' + last
    return full_name.title()
  • Om te controleren of bovenstaande functie ook werkt, kun je een programma maken dat deze functie gebruikt
def get_formatted_name(first, last):
    """Maak een net opgemaakte naam"""
    full_name = first + ' ' + last
    return full_name.title()

print("Enter 'q' om te stoppen")
while True:
    first = input("\nType een voornaam: ")
    if first == 'q':
        break

    last = input("Type een achternaam: ")
    if last == 'q':
        break

    formatted_name = get_formatted_name(first, last)
    print("\tDe opgegeven naam is : " + formatted_name + '.')
  • Bovenstaande code verwerkt de namen goed
  • Python heeft een efficiënte manier om het testen van de uitvoer van een functie te automatiseren
  • De module unittest uit de standaard bibliotheek van Python biedt hulpmiddelen voor het testen van je code
  • Een unit-test controleert of een specifiek aspect van het gedrag van je functie correct is
  • Een test-case is een verzameling unit-tests die gezamenlijk aantonen dat een functie goed werkt
  • Een goede test-case houdt rekening met alle soorten invoer die een functie kan ontvangen
  • Een test-case met full coverage kan bij grote projecten intimiderend zijn
  • Vaak is het voldoende om tests te schrijven voor het kritieke gedrag van je code
import unittest

def get_formatted_name(first, last):
    """Maak een net opgemaakte naam"""
    full_name = first + ' ' + last
    return full_name.title()


class NamesTestCase(unittest.TestCase):
    """Test voor de get_formatted_name()-functie"""

    def test_first_last_name(self):
        formatted_name = get_formatted_name('claes', 'compaen')
        self.assertEqual(formatted_name, 'Claes Compaen')

unittest.main()
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
  • Je kunt elke naam kiezen als naam voor je class, maar het beste verwerk je Test in de naam
  • De class moet overerven van de class unittest.TestCase
  • Elke methode die begint met test_ in de naamgeving zal automatisch worden uitgevoerd bij het aanroepen van de class
  • Er zit één methode in de class die controleert of de naam correct wordt geretourneerd
  • Je kunt in de test-methode de functie aanroepen die je wilt testen
  • Stel je wilt de functie aanpassen met een tweede voornaam
def get_formatted_name(first, middle, last):
    """Maak een net opgemaakte naam"""
    full_name = first + ' ' + middle + ' ' + last
    return full_name.title()
  • Deze versie werkt wel voor mensen met een tweede naam maar werkt niet meer voor mensen zonder tweede naam
  • Als je nu de test erop zou uitvoeren krijgen we een melding dat de test niet slaagt
E
======================================================================
ERROR: test_first_last_name (__main__.NamesTestCase.test_first_last_name)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "<exec>", line 13, in test_first_last_name
TypeError: get_formatted_name() missing 1 required positional argument: 'last'
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (errors=1)
  • De uitgebreide foutmelding laat precies zien welke unit test een error geeft
  • Wanneer er veel unit tests worden uitgevoerd, is een uitgebreide foutmelding belangrijk
  • Er wordt ook vermeld hoeveel unit tests er zijn uitgevoerd: Ran 1 test
  • Repareer de fout door de tweede naam optioneel te maken
def get_formatted_name(first, last, middle=''):
    """Maak een net opgemaakte naam"""
    if middle:
        full_name = first + ' ' + middle + ' ' + last
    else:
        full_name = first + ' ' + last
    return full_name.title()
  • Na deze wijziging zal de test weer goed werken als iemand alleen één voornaam en achternaam opgeeft
  • Maar deze nieuwe mogelijkheid moet op zijn beurt ook getest worden
import unittest

def get_formatted_name(first, last, middle=''):
    """Maak een net opgemaakte naam"""
    if middle:
        full_name = first + ' ' + middle + ' ' + last
    else:
        full_name = first + ' ' + last
    return full_name.title()


class NamesTestCase(unittest.TestCase):
    """Test voor de get_formatted_name()-functie"""

    def test_first_last_name(self):
        """Test één voornaam en achternaam"""
        formatted_name = get_formatted_name('claes', 'compaen')
        self.assertEqual(formatted_name, 'Claes Compaen')

    def test_first_last_middle_name(self):
        """Test twee voornamen en achternaam"""
        formatted_name = get_formatted_name('Jan', 'Reyning', 'Erasmus')
        self.assertEqual(formatted_name, 'Jan Erasmus Reyning')


unittest.main()
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
  • Het is logischer om de test class niet bij het programma te zetten maar in een aparte module
  • De naam van methoden in de test class mogen best lang zijn, ze worden immers automatisch aangeroepen
02 - Een class testen
  • Python biedt een aantal assert-methoden in de unittest.TestCase-class
  • Assert-methoden testen of een conditie, waarvan jij denkt dat die waar moet zijn, ook daadwerkelijk waar is
Methode Doel Voorbeeld
assertEqual(a, b) Controleert of a == b self.assertEqual(2 + 2, 4)
assertNotEqual(a, b) Controleert of a != b self.assertNotEqual(len("abc"), 5)
assertTrue(x) Controleert of x waar is self.assertTrue(3 < 5)
assertFalse(x) Controleert of x onwaar is self.assertFalse(10 in [1, 2, 3])
assertIn(a, b) Controleert of a in b zit self.assertIn("py", "python")
assertNotIn(a, b) Controleert of a niet in b zit self.assertNotIn(4, [1, 2, 3])
assertGreater(a, b) Controleert of a > b self.assertGreater(10, 5)
assertGreaterEqual(a, b) Controleert of a ≥ b self.assertGreaterEqual(5, 5)
assertLess(a, b) Controleert of a < b self.assertLess(2, 10)
assertLessEqual(a, b) Controleert of a ≤ b self.assertLessEqual(3, 3)
assertAlmostEqual(a, b) Vergelijkt afrondingsverschil (handig bij floats) self.assertAlmostEqual(0.1 + 0.2, 0.3)
assertRaises(Exception) Controleert of een fout wordt opgegooid with self.assertRaises(ValueError): int("abc")
  • Het testen van een class is vergelijkbaar met het testen van een functie
  • In onderstaande code wordt de class getest met invoer van de gebruiker
class Rekenmachine:
    """Eenvoudige rekenmachine die optelt, vermenigvuldigt
    en bijhoudt hoeveel berekeningen zijn uitgevoerd."""

    def __init__(self):
        self.aantal_bewerkingen = 0

    def tel_op(self, x, y):
        self.aantal_bewerkingen += 1
        return x + y

    def vermenigvuldig(self, x, y):
        self.aantal_bewerkingen += 1
        return x * y

    def reset(self):
        """Zet de teller terug naar 0."""
        self.aantal_bewerkingen = 0


# Gebruik van de class Rekenmachine met gebruikersinvoer

rekenmachine = Rekenmachine()

print("Welkom bij de rekenmachine!")
print("Kies: 1 = optellen, 2 = vermenigvuldigen, q = stoppen")

while True:
    keuze = input("\nMaak een keuze: ")

    if keuze == "q":
        print("Programma beëindigd.")
        break

    if keuze not in ("1", "2"):
        print("Ongeldige keuze. Kies 1, 2 of q.")
        continue

    try:
        x = float(input("Eerste getal: "))
        y = float(input("Tweede getal: "))
    except ValueError:
        print("Je moet wel een geldig getal invoeren!")
        continue

    if keuze == "1":
        resultaat = rekenmachine.tel_op(x, y)
        print("Uitkomst:", resultaat)

    elif keuze == "2":
        resultaat = rekenmachine.vermenigvuldig(x, y)
        print("Uitkomst:", resultaat)

    print("Aantal uitgevoerde bewerkingen:", rekenmachine.aantal_bewerkingen)
  • De class kan ook getest worden met een unit-test
  • Het is een goed idee om de unit-test in een andere module te zetten voor het overzicht
# rekenmachine.py

class Rekenmachine:
    """Eenvoudige rekenmachine die optelt, vermenigvuldigt
    en bijhoudt hoeveel berekeningen zijn uitgevoerd."""

    def __init__(self):
        self.aantal_bewerkingen = 0

    def tel_op(self, x, y):
        self.aantal_bewerkingen += 1
        return x + y

    def vermenigvuldig(self, x, y):
        self.aantal_bewerkingen += 1
        return x * y

    def reset(self):
        """Zet de teller terug naar 0."""
        self.aantal_bewerkingen = 0

# test_rekenmachine.py

import unittest
from rekenmachine import Rekenmachine


class TestRekenmachine(unittest.TestCase):
    """Unittests voor de Rekenmachine-class."""

    def setUp(self):
        """Wordt vóór elke test uitgevoerd."""
        self.rm = Rekenmachine()

    def test_tel_op_geeft_juiste_som(self):
        resultaat = self.rm.tel_op(2, 3)
        self.assertEqual(resultaat, 5)

        resultaat = self.rm.tel_op(-1, 1)
        self.assertEqual(resultaat, 0)

        resultaat = self.rm.tel_op(2.5, 0.5)
        self.assertAlmostEqual(resultaat, 3.0)

    def test_vermenigvuldig_geeft_juiste_product(self):
        resultaat = self.rm.vermenigvuldig(4, 5)
        self.assertEqual(resultaat, 20)

        resultaat = self.rm.vermenigvuldig(-2, 3)
        self.assertEqual(resultaat, -6)

        resultaat = self.rm.vermenigvuldig(2.5, 2)
        self.assertAlmostEqual(resultaat, 5.0)

    def test_aantal_bewerkingen_loopt_op(self):
        # In het begin: 0
        self.assertEqual(self.rm.aantal_bewerkingen, 0)

        self.rm.tel_op(1, 2)          # 1 bewerking
        self.assertEqual(self.rm.aantal_bewerkingen, 1)

        self.rm.vermenigvuldig(2, 3)  # 2e bewerking
        self.assertEqual(self.rm.aantal_bewerkingen, 2)

        self.rm.tel_op(10, 10)        # 3e bewerking
        self.assertEqual(self.rm.aantal_bewerkingen, 3)

    def test_reset_zet_teller_terug_naar_nul(self):
        self.rm.tel_op(1, 1)
        self.rm.vermenigvuldig(2, 2)
        self.assertGreater(self.rm.aantal_bewerkingen, 0)

        self.rm.reset()
        self.assertEqual(self.rm.aantal_bewerkingen, 0)


if __name__ == "__main__":
    unittest.main()
Oefening
Quiz

Vraag 1. Welke module gebruik je in Python om unit-tests te schrijven?




✅ Goed! unittest zit in de standaardbibliotheek van Python.
❌ Niet helemaal. Voor dit hoofdstuk gebruiken we unittest uit de standaardbibliotheek.

Vraag 2. Van welke class moet je testclass erven bij unittest?




✅ Klopt. Je testclass erft van unittest.TestCase.
❌ Let op. De basisclass is unittest.TestCase.

Vraag 3. Welke methodenaam wordt automatisch als test uitgevoerd door unittest?




✅ Goed! Methoden die starten met test_ worden automatisch uitgevoerd.
❌ Nee. Alleen methoden die starten met test_ telt unittest als tests.

Vraag 4. Welke assert controleert of twee waarden gelijk zijn?




✅ Precies. assertEqual controleert of a == b.
❌ Niet goed. Gebruik self.assertEqual(a, b) om gelijkheid te testen.

Vraag 5. Welke onderdelen horen bij een minimale unittest-file?




✅ Goed! Dit zijn de belangrijkste bouwstenen van een minimale unittest.
❌ Niet helemaal. Je hebt unittest, een TestCase-class en een test_-methode nodig.

Vraag 6. Wat doet deze regel aan het einde van een testbestand?

unittest.main()



✅ Klopt. unittest.main() zoekt tests en voert ze uit.
❌ Nee. unittest.main() is de “runner” die de tests uitvoert.

Vraag 7. Je wil testen of een fout wordt opgegooid. Welke constructie hoort daarbij?




✅ Goed! Met assertRaises test je of een exception voorkomt.
❌ Niet juist. Gebruik with self.assertRaises(...): om exceptions te testen.

Vraag 8. Wat is het doel van setUp() in een unittest-class?




✅ Precies. setUp() draait vóór elke testmethode.
❌ Let op. setUp() gebruik je om per test alvast objecten/data klaar te zetten.

Vraag 9. Welke output betekent meestal dat al je tests geslaagd zijn?




✅ Goed! OK betekent: alle tests zijn geslaagd.
❌ Nee. E is error, F is failure. Bij succes zie je meestal OK.

Vraag 10. Waarom zet je tests vaak in een apart bestand, zoals test_rekenmachine.py?




✅ Klopt. Het houdt je project overzichtelijk en onderhoudbaar.
❌ Niet helemaal. Je zet tests apart vooral voor overzicht en onderhoud.

AI in de praktijk - Een e-mailfunctie testen
  • In dit hoofdstuk heb je geleerd hoe je functies en classes kunt testen met unittest
  • In de praktijk is het belangrijk om te controleren of code blijft werken nadat je wijzigingen hebt gemaakt
  • Ook AI-gegenereerde code kun je automatisch controleren met tests
  • Stel dat je een functie hebt die een standaard ontvangstbevestiging maakt:
Beste Jan,

Wij hebben uw bericht ontvangen.
Wij nemen zo spoedig mogelijk contact met u op.

Met vriendelijke groet,
Training Minds
  • Gebruik onderstaande prompt in een AI-tool
Je bent een Python-assistent voor beginners.

Maak een Python-programma dat een standaard ontvangstbevestiging maakt
en deze functie test met unittest.

Het programma moet:

- een functie bevatten met de naam make_email()
- een parameter gebruiken voor de naam van de ontvanger
- een standaard e-mailtekst teruggeven met return
- de naam van de ontvanger verwerken in de aanhef

Maak daarnaast een unittest die controleert:

- dat de naam van de ontvanger voorkomt in de e-mail
- dat de tekst "Wij hebben uw bericht ontvangen" voorkomt
- dat de tekst eindigt met "Training Minds"

Gebruik:

- functies
- parameters
- return
- strings
- unittest
- assertIn()
- assertTrue()

Gebruik geen:

- input()
- loops
- dictionaries
- classes behalve de unittest-class
- externe modules
- csv
- pandas
- json

Geef alleen de Python-code terug.
  • Kopieer de prompt naar ChatGPT, Claude, Gemini of een andere AI-tool
  • Kopieer de gegenereerde code naar de editor
  • Voer de code uit
  • Controleer of alle tests slagen
  • Controleer of je de melding OK krijgt
  • Verander daarna zelf:
    • één tekstregel in de e-mail
    • de bedrijfsnaam
    • de naam van de ontvanger in de test
Reflectievragen
  1. Welke functie wordt getest?
  2. Welke testmethode wordt automatisch uitgevoerd?
  3. Waarom begint de testmethode met test_?
  4. Welke assert-methoden worden gebruikt?
  5. Wat controleert assertIn()?
  6. Wat gebeurt er als je de e-mailtekst wijzigt?
  7. Waarom zijn tests handig bij wijzigingen in bestaande code?
  8. Kun je vertrouwen op AI-code zonder tests?
  9. Begrijp je elke regel van de testcode?
Extra uitdaging
  • Breid de functie uit met een tweede parameter voor het onderwerp van de e-mail
  • Laat AI ook een extra unittest maken voor deze nieuwe functionaliteit
Pas de functie aan zodat deze ook een parameter krijgt:

onderwerp

Verwerk het onderwerp in de e-mailtekst.

Maak daarnaast een extra unittest die controleert
of het onderwerp voorkomt in de e-mail.

Gebruik unittest.

Geef alleen de Python-code terug.
  • Controleer of alle tests nog steeds slagen
  • Voeg zelf nog een extra test toe