Unit tests met PyTest
Betrouwbare en beheersbare code zorgen voor besparing van tijd, kosten, en het voorkomen van grote fouten. In deze blog geven we een introductie in PyTest, een tool voor het doen van unit tests.
Betrouwbare en beheersbare code zorgen voor besparing van tijd, kosten, en het voorkomen van grote fouten. In het software-landschap worden velen testen gebruikt, en deze dragen bij aan die betrouw- en beheersbaarheid. Het testen van alles wat je ontwikkelt, bespaart aan het eind van de rit veel tijd en geld. Hoe hoog die besparing is hangt natuurlijk af van de waarde van de output en de kosten die gemoeid zijn met de continue ontwikkeling. In deze blog geven we een introductie in PyTest, een tool voor het doen van unit tests, om daarbij te helpen.

Maar hoe en wat moet ik testen dan, vraagt u zich inmiddels af? Nou.., daar is geen kort of simpel antwoord op. Testen kan namelijk op allerlei manieren. Zo zijn er de unit tests, integration tests, functional tests, end-to-end tests, performance tests en user-acceptance tests. Vervolgens kan dit ook nog worden opgesplitst naar handmatige en geautomatiseerde tests. En zo kunnen we even doorgaan… Daarbij komt vaak kijken dat software testen wordt gezien als een noodzakelijk kwaad vanuit de business en niet als een mogelijkheid om betrouw- en beheersbaarheid te verhogen. Gezien de grote waarde van testen voor zowel de business als ontwikkelaars wordt er in deze blog een introductie gegeven. De focus hierbij is op het laagdrempelige (lage instapkosten) unit testen, en dan specifiek in Python als veelgebruikte programmeertaal. Er zal hierbij gebruikt worden gemaakt van PyTest.
De onderwerpen die langs zullen komen zijn:
- Waarom en wat is unit testen?
- De basis van PyTest
- Valideren van fouten die je verwacht
- Testen met verschillende input waardes
Benodigdheden
- Python
Waarom en wat is unit testen?
In dit eerste deel zal worden beschreven wat unit testen inhoud en welke voordelen dit biedt. Hierdoor wordt ook duidelijk wat de voordelen zijn van dit soort processen in controleerbare code vast te leggen ten opzichte van de oncontroleerbare processen die vaak gebaseerd zijn op Excel bestanden.
Unit testen
Bij unit tests worden de individuele componenten (units) van een stuk software getest. Hiermee kan worden gecontroleerd of de code doet wat er van verwacht wordt. Er wordt dus (verschillende) input gedefinieerd met een bijbehorende verwachte output. Vervolgens kan de output van de component (met input) vergeleken worden met de verwachte output.
De focus moet zijn om één specifiek ding te testen, hiermee wordt de ontwikkelaar ook gedwongen om zijn componenten maar één specifieke taak te laten uitvoeren. Hierdoor ontstaat ook meteen schone en leesbare code (#win-win).
In sommige gevallen worden componenten ook gebouwd door te beginnen met het maken van een test. Dit wordt ook wel ‘test-driven-development’ genoemd. Dit wordt gedaan omdat in de test de verwachte output van een component wordt gedefinieerd. Vervolgens kan de component gebouwd worden totdat de test slaagt. Hiermee wordt de ontwikkelaar ook gedwongen om eerst goed na te denken over de verwachte uitkomst, in plaats van vol enthousiasme te gaan coderen zonder plan.
Voordelen op een rij
Naast de eerder benoemde voordelen gericht op de ontwikkelaar zijn er minstens net zo belangrijke voordelen voor de business. Door het gebruik van testen wordt niet alleen de kwaliteit van de code verhoogd maar ook die van het algehele product. Het wordt betrouwbaarder, doordat meer fouten al tijdens de ontwikkeling worden afgevangen. Hiermee zijn er ook minder risico’s met het gebruik van het product. Daarnaast is er een kleinere kans dat er fouten of issues in een later stadium opgelost moet worden. Dit zorgt altijd voor minder kosten. In de toekomst zullen eventuele doorontwikkelingen beter kunnen worden ingeschat, minder tijd in beslag nemen en dus tot een tevreden gebruiker leiden.
- Verhoogt kwaliteit van code en product
- Verlaagt risico’s
- Verhoogt tevredenheid van ontwikkelaar en gebruiker
- Verlaagt kosten
- Verlaagt totale doorlooptijd
De basis van PyTest
Een van de mogelijkheden om unit tests te gebruiken in Python is door middel van de package PyTest. Hierbij een korte introductie tot PyTest. Er wordt gebruik gemaakt van een functie en er worden twee testen uitgevoerd:
- Functie (unit) die getest moet worden (calculate())
- Test die faalt (test_failing_answer())
- Test die niet faalt (test_successful_answer())
Code
# content of test_script.py
def calculate(x):
return x + 41
def test_failing_answer():
# Failing test
assert calculate(x=1) == 43
def test_successful_answer():
# Successful test
assert calculate(x=1) == 42
# Run script in command line
$ pytest tests/test_script.py
# Output
===================== FAILURES =====================
________________ test_failing_answer ________________
def test_failing_answer():
# Failing test
> assert calculate(x=1) == 43
E assert 42 == 43
E + where 42 = calculate(x=1)
tests/test_script.py:8: AssertionError
=============== short test summary info ===============
FAILED tests/test_script.py::test_failing_answer - assert 42 == 43
============ 1 failed, 1 passed in 0.04s =============
Code ter introductie van PyTest
Dit zijn simpele tests die twee getallen met elkaar vergelijken, met PyTest kan je echter ook complexere tests uitvoeren. Deze zijn verder terug te vinden in de documentatie.
Valideren van fouten die je verwacht
In de introductie bouwden we tests waarbij de code succesvol draait en de uitkomst gecontroleerd wordt. Tijdens het ontwikkelen komen er ook situaties voor waarbij we bewust willen dat de code NIET succesvol draait. In Python zorgt dit voor errors die de code stopt, en hierdoor kan de assert niet plaats vinden. Gelukkig biedt PyTest ook de mogelijkheid om te testen of de code juist een error geeft bij bijvoorbeeld een verkeerde input.
Code
# content of test_script.py
import pytest
def calculate(x):
if isinstance(x, str):
raise TypeError("Input is not numerical")
return x + 41
def test_exception_answer():
with pytest.raises(TypeError):
calculate("Test")
# Run script in command line
$ pytest tests/test_script.py
# Output
tests/test_script.py . [100%]
============ 1 passed in 0.01s ============
Testen voor fouten in PyTest
Hier is te zien hoe een fout afgevangen kan worden. Er kan zelfs een stap verder gegaan worden door ook gebruik te maken van zelfgedefinieerde fouten. Hierdoor kan je de tests nog specifieker inrichten.
Testen met verschillende input waardes
Eerder is al benoemd dat je de functies wil kunnen testen met verschillende input waardes. De functie moet namelijk de juiste output geven voor verschillende input. PyTest heeft ervoor gezorgd dat er niet steeds een nieuwe test geschreven hoeft te worden voor iedere input, of een omslachtige for-loop om de test heen gebouwd hoeft te worden. PyTest biedt namelijk de @pytest.mark.parametrize decorator. De werking hiervan zie je hieronder.
Code
# content of test_script.py
import pytest
def create_text(number, text):
result = f"This text {text} and that number {number} as well!"
return result
@pytest.mark.parametrize(
"number, text, expected",
[
(42, "is nice", "This text is nice and that number 42 as well!"),
(666, "is cool", "This text is cool and that number 666 as well!")
]
)
def test_parametrize_answer(number, text, expected):
result = create_text(number, text)
assert result == expected
# Run script in command line
$ pytest tests/test_script.py
# Output
tests/test_script.py . [100%]
======================== 2 passed in 0.01s ========================
Testen met verschillende input waardes in PyTest
In de output is nu ook te zien dat er twee tests succesvol zijn afgerond, terwijl er maar een test gedefinieerd is. De twee verschillende inputs zorgen ervoor dat dit gezien wordt als twee testen.
Ten slotte
De betrouw- en beheersbaarheid van de code kan verhoogd worden door middel van testen en daarmee tot de verhoging van kwaliteit, verlaging van kosten en verlaging van risico’s. In deze blog is er een introductie gedaan in de wereld van het testen om te laten zien dat het allemaal zo eng niet is. Er is gefocust op het laagdrempelige unit testen en hoe dit te kunnen implementeren in een Python project met PyTest. Hopelijk ben je overtuigd van het nut van testen voor de business en/of heb je een mooie introductie tot PyTest weten te krijgen.
Start met testen en verhoog die betrouw- en beheersbaarheid!
Did you find this article interesting?
📃 Check out our previous blog as well: Stap voor stap: Financiële consolidatie met Python
✅ Make sure to follow us on LinkedIn for new content!

