Når du programmerer, ønsker du at programmet skal gjøre det du forventer. Men hvordan vet du at koden faktisk fungerer? Og hvordan kan du kontrollere at en endring ikke har ødelagt noe som fungerte tidligere?
For å undersøke dette kan vi teste koden vår. Test Driven Development (TDD) er en arbeidsmetode der tester brukes aktivt gjennom utviklingen av et program.
Del 1: Hva er TDD?
Tenk deg at du utvikler en nettbutikk. Før du skriver koden, lager du hypoteser om hvordan den skal fungere.
For eksempel:
- Tre varer til 100 kroner per stykk skal koste 300 kroner.
- Fem varer til 0 kroner skal koste 0 kroner.
- En vare til -500 kroner skal ikke godtas, fordi en vare ikke skal ha en negativ pris.
Disse hypotesene beskriver hvordan du forventer at nettbutikken skal oppføre seg. Du kan gjøre dem om til tester som undersøker om programmet følger forventningene dine.
I Test Driven Development (TDD) skriver du tester før du implementerer funksjonaliteten. Deretter utvikler du koden i små steg og bruker testene til å kontrollere at programmet oppfører seg som forventet.
Arbeidsflyten kalles Red, Green, Refactor:
Red, Green, Refactor
- 🔴 Red: Skriv en test som feiler.
- 🟢 Green: Skriv nok kode til at testen består.
- 🔵 Refactor: Rydd opp i koden uten å endre oppførselen.
Deretter gjentar du syklusen mens programmet utvikles.
Tester undersøker forventningene dine
En test kontrollerer om programmet oppfører seg slik du forventer. Den beviser ikke automatisk at programmet er riktig.
Derfor må du også tenke gjennom om hypotesene dine er fornuftige. Hvis negative priser ikke skal være tillatt, må du også kontrollere at programmet avviser dem.
Del 2: Manuell testing og automatiserte tester
Det finnes flere måter å teste et program på. To vanlige tilnærminger er manuell testing og automatiserte tester.
Manuell testing
Ved manuell testing kjører du programmet selv, prøver ulike handlinger og undersøker om resultatet er riktig.
I nettbutikken kan du for eksempel kjøpe en vare og kontrollere om pengene i lommeboka blir oppdatert riktig.
Dette er nyttig når du vil undersøke hvordan programmet fungerer i praksis. Ulempen er at du må gjenta handlingene selv hver gang du vil kontrollere resultatet.
Automatisert testing
I stedet for å kontrollere resultatet manuelt kan du skrive kode som utfører kontrollen for deg.
Se på denne funksjonen:
1 2 | |
Funksjonen beregner totalprisen. Vi forventer at tre varer til 100 kroner skal koste 300 kroner. Dette kan vi kontrollere automatisk ved å bruke assert:
1 2 3 4 | |
Python utfører kontrollen når testkoden kjøres. Hvis resultatet er 300, består testen. Hvis resultatet er noe annet, får vi en AssertionError.
| Manuell testing | Automatisert testing |
|---|---|
| Du prøver programmet selv. | Koden utfører kontrollene. |
| Du undersøker resultatene manuelt. | Testene sammenligner resultatene med forventningene. |
| Du gjentar handlingene selv. | De samme testene kan kjøres igjen når som helst. |
Trenger vi fortsatt manuell testing?
Ja. Manuell og automatisert testing utfyller hverandre. Manuell testing er nyttig når du vil undersøke hvordan programmet oppleves og brukes. Automatiserte tester er særlig nyttige når du vil kontrollere bestemte resultater raskt og gjentatte ganger.
Del 3: Hvordan fungerer assert?
I forrige del brukte vi assert til å kontrollere resultatet fra en funksjon. Men hva skjer egentlig når vi kjører en slik test?
Python evaluerer uttrykket etter assert og undersøker om det er sant.
1 | |
Python kaller funksjonen med argumentene 100 og 3. Funksjonen returnerer 300, og Python sammenligner denne verdien med 300.
- Hvis resultatet er
300, består testen, og programmet fortsetter uten en feilmelding. - Hvis resultatet er noe annet, får vi en
AssertionError, og programmet stopper ved denne testen.
Hva hvis funksjonen har en feil?
Tenk deg at vi ved en feil legger til én krone på totalprisen:
1 2 | |
Testen vår vil nå feile:
1 | |
Funksjonen returnerer 301, mens testen forventer 300. Testen avdekker dermed en feil vi kanskje ikke hadde oppdaget med en gang ved manuell testing.
Testen er bare så god som forventningen
assert vet ikke hva riktig resultat skal være. Den kontrollerer bare om resultatet stemmer med verdien du har skrevet i testen.
Hvis du skriver assert totalpris(100, 3) == 301, vil testen bestå med den feilaktige funksjonen ovenfor. Derfor må du tenke gjennom hva funksjonen faktisk skal gjøre før du skriver testen.
Test flere situasjoner
Én test undersøker bare én situasjon. Vi kan derfor skrive flere tester for å kontrollere ulike forventninger:
1 2 3 | |
Når vi kjører testene, kontrollerer Python hver påstand i rekkefølge. Hvis en test feiler, stopper programmet ved den første feilen.
Det er også viktig å tenke på situasjoner der funksjonen ikke skal godta verdiene den får. Hvis nettbutikken ikke skal tillate negative priser, bør vi ha en test som kontrollerer at slike priser blir avvist.
En bestått test betyr ikke at alt fungerer
Testene kontrollerer bare situasjonene du har skrevet tester for. Funksjonen kan gi riktige resultater for vanlige verdier, men likevel ha feil i andre situasjoner.
Derfor bør du teste flere relevante situasjoner, også uvanlige eller ugyldige verdier.
Del 4: Test funksjoner fra andre filer
I større programmer er det vanlig å dele koden opp i flere filer. Da kan vi samle funksjoner som hører sammen, og teste dem uten å starte hele programmet.
I et enkelt nettbutikkprosjekt kan filene for eksempel være organisert slik:
src/
├── main.py
├── store.py
└── test_store.py
main.pystarter nettbutikken og håndterer samspillet med brukeren.store.pyinneholder funksjoner for blant annet kjøp og salg.test_store.pyinneholder tester for funksjonene.
Importer en funksjon
Hvis store.py inneholder funksjonen totalpris(), kan vi importere den i testfilen:
1 2 3 4 | |
Linjen from store import totalpris henter funksjonen fra filen store.py, slik at vi kan bruke den i test_store.py.
I dette eksempelet ligger filene i samme mappe. Hvis de ligger i en undermappe, må importen og måten testene kjøres på tilpasses prosjektstrukturen.
Hvorfor teste funksjoner direkte?
Hvis du tester en funksjon gjennom main.py, må du kanskje starte hele programmet og gå gjennom menyer eller skrive inn verdier.
Når du importerer funksjonen direkte, kan du sende inn bestemte verdier og kontrollere resultatet uten å starte hele nettbutikken. Det gjør testingen enklere å gjenta.
Kjør testfilen
Hvis testfilen ligger i src/ og prosjektet bruker uv, kan du kjøre den fra prosjektmappen med:
uv run python src/test_store.py
Python kjører koden i test_store.py, inkludert testene som bruker funksjoner fra store.py.
Hvis alle assert-kontrollene består, fortsetter programmet til neste linje. Hvis en kontroll feiler, stopper programmet med en AssertionError.
Test etter endringer
Når du endrer en funksjon, bør du kjøre testene på nytt. Da kan du oppdage om endringen har ført til feil, også i funksjonalitet som tidligere fungerte.
Oppsummering
- TDD: En arbeidsmetode der du skriver tester før du implementerer funksjonaliteten.
- Hypoteser: Forventninger om hvordan programmet skal oppføre seg.
- Manuell testing: Du bruker programmet selv og kontrollerer resultatene.
- Automatisert testing: Kode utfører kontroller av forventede resultater.
assert: Lar Python kontrollere om et uttrykk er sant.import: Gjør det mulig å bruke funksjoner fra andre Python-filer.- Red, Green, Refactor: Skriv en test som feiler, få den til å bestå, og forbedre koden uten å endre oppførselen.
Vil du lære mer om testing?
I denne introduksjonen bruker vi assert og kjører testfilen direkte med Python.
Senere kan du utforske pytest, et eget testverktøy som gir flere muligheter for å organisere og kjøre tester. Du trenger ikke pytest for å bruke assert eller forstå grunnprinsippene i TDD.