Enhetstesting, integrasjonstesting og testdrevet utvikling.
Trygghet til å endre
Testing er en kritisk del av programvareutvikling. Godt testede programmer har færre feil, er enklere å vedlikeholde, og gir utviklere trygghet når de gjør endringer – du tør å endre kode når testene forteller deg om du brøt noe. Men ikke alle tester er like, og en god strategi balanserer dem. Det visualiseres ofte med testpyramiden.
Nederst i pyramiden, og flest i antall, ligger enhetstester (unit tests). De tester én enkelt funksjon eller metode isolert. De kjører lynraskt (millisekunder), er enkle å skrive og vedlikeholde, finner bugs tidlig, og fungerer som dokumentasjon av forventet oppførsel. I midten ligger integrasjonstester, som tester samspillet mellom flere komponenter – for eksempel database og backend, eller flere moduler sammen. De er færre fordi de er tregere og mer komplekse. Helt på toppen, og færrest, ligger end-to-end-tester (E2E), som tester hele systemet fra brukerens perspektiv ved å simulere en ekte bruker.
Formen på pyramiden er ikke tilfeldig: du vil ha mange raske, billige enhetstester i bunnen, et moderat antall integrasjonstester i midten, og bare noen få trege E2E-tester på toppen. En omvendt pyramide – få enhetstester og mange E2E-tester – gir treg, skjør testing som er vanskelig å vedlikeholde.
Enhetstesting med unittest og pytest
La oss skrive faktiske tester. Python har to populære verktøy. Det innebygde unittest organiserer tester i klasser som arver fra unittest.TestCase, der hver test er en metode som bruker assert-metoder som self.assertEqual. Vil du teste en add-funksjon, skriver du self.assertEqual(add(2, 3), 5). En viktig del av god testing er å teste grensetilfeller og feil: en divide-funksjon bør for eksempel testes på at den kaster en feil når du deler på null.
Det andre verktøyet, pytest, er svært populært på grunn av enklere syntaks. I stedet for klasser og spesielle metoder bruker du bare vanlige funksjoner og det innebygde assert:
def test_add_positive():
assert add(2, 3) == 5
def test_divide_by_zero():
with pytest.raises(ValueError, match="Kan ikke dele på null"):
divide(10, 0)Legg merke til pytest.raises, som sjekker at koden faktisk kaster den forventede feilen. Pytest har også et kraftig triks kalt parametriserte tester: med dekoratøren @pytest.mark.parametrize kjører du samme test med mange ulike input på én gang, slik at du dekker flere tilfeller uten å gjenta koden. Begge verktøy oppnår det samme – automatisk verifisering av at koden gjør det den skal – men pytest er ofte raskere å skrive.
Testdrevet utvikling
Et kraftig prinsipp snur den vanlige rekkefølgen på hodet: Test-Driven Development (TDD), der du skriver testen før koden. Det høres bakvendt ut, men det er overraskende effektivt. TDD følger en syklus i tre steg, ofte kalt rød-grønn-refaktorer.
Først Red: du skriver en test for funksjonaliteten du ønsker, og kjører den. Den feiler – naturligvis, for funksjonen finnes ikke ennå. Skal du lage validate_email, skriver du først tester som assert validate_email("bruker@example.com") == True og assert validate_email("brukerexample.com") == False. De feiler rødt. Deretter Green: du skriver den minimale koden som får testene til å passere – ikke mer enn nødvendig. For e-postvalidatoren kan det være å sjekke at strengen inneholder @ og ikke slutter på @. Nå blir testene grønne. Til slutt Refactor: når testene passerer, kan du rydde og forbedre koden trygt, fordi testene fanger opp hvis du ødelegger noe.
Hvorfor virker TDD? Fordi det tvinger deg til å tenke gjennom hva koden skal gjøre før du skriver den, og fordi du alltid har et sikkerhetsnett av tester. Du ender også opp med 100 prosent testdekning av det du faktisk bygget, siden ingen kode skrives uten en test som krever den. Sammen med vanen om å skrive tester kontinuerlig gjennom prosjektet – ikke vente til slutten – gir dette robust, vedlikeholdbar programvare. Og en god regel: kjør testene automatisk, gjerne ved hver endring, så feil oppdages med en gang.
Oppsummering
Testing gir oss trygghet til å endre kode. Testpyramiden anbefaler mange raske enhetstester i bunnen, færre integrasjonstester i midten, og bare noen få E2E-tester på toppen. I Python skriver vi enhetstester med innebygde unittest eller med det enklere pytest, der pytest.raises sjekker feil og @parametrize kjører samme test med mange input.
Testdrevet utvikling (TDD) snur rekkefølgen: Red (skriv test som feiler), Green (minimal kode som passerer), Refactor (rydd trygt). Det tvinger fram klar tenkning og gir et sikkerhetsnett. Sammen med kontinuerlig og automatisk testing bygger dette robust programvare. I neste kapittel ser vi på versjonskontroll og samarbeid med Git.
Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.