Enhetstesting, integrasjonstesting og testdrevet utvikling.
Testing er ein kritisk del av programvareutvikling. Godt testa program har færre feil, er enklare å vedlikehalde og gir utviklarar tryggleik når dei gjer endringar. I dette kapittelet skal vi sjå på ulike typar testing – frå einingstestar av individuelle funksjonar til integrasjonstestar av heile systemet.
Vi skal også sjå på Test-Driven Development (TDD) og kva code review har å seie for kvalitetssikring.
Testpyramiden viser korleis ulike typar testar bør balanserast:
/\
/ \ E2E tests (få)
/____\
/ \ Integrasjonstester (middels)
/________\
/ \ Enhetstester (mange)
/____________\Einingstestar (Unit tests)
Testar éin enkelt funksjon eller metode isolert:
- Rask køyretid (millisekund)
- Enkle å skrive og vedlikehalde
- Finn bugs tidleg
- Fungerer som dokumentasjon av forventa åtferd
Bibliotek: unittest (Python standard), pytest
Integrasjonstestar
Testar samspelet mellom fleire komponentar:
- Database + backend
- API + frontend
- Fleire modular saman
End-to-End (E2E) testar
Testar heile systemet frå perspektivet til brukaren:
- Simulerer ekte brukarscenario
- Treg køyretid (sekund/minutt)
- Meir kompleks å skrive og vedlikehalde
- Gir høgast tillit til at systemet fungerer
Verktøy: Selenium, Playwright, Cypress
Test-Driven Development (TDD)
TDD er ein utviklingsmetode der testar blir skrivne før koden:
1. Red – Skriv ein test som feilar (funksjonen finst ikkje enno)
2. Green – Skriv minimal kode for å få testen til å passere
3. Refactor – Forbetre koden utan å endre åtferd
Fordelar:
- Tvingar deg til å tenkje på grensesituasjonar
- All kode er testa by design
- Enklare å refaktorere seinare
Ulemper:
- Tek lengre tid i starten
- Krev disiplin
Code Review
Code review er prosessen der andre utviklarar les og kommenterer koden din før han blir merga:
Kva ser vi etter:
- Logiske feil
- Manglande feilhandtering
- Usikker kode (SQL injection, XSS)
- Dårleg namngiving eller struktur
- Manglande testar
Best practices:
- Hald pull requests små (< 400 linjer)
- Vær konstruktiv, ikkje personangrep
- Forklar kvifor, ikkje berre kva
- Anerkjenn god kode
Testdekningsgrad (Code Coverage)
Måler kor stor prosentdel av koden som blir køyrd av testane:
- 80-90% blir rekna som godt
- 100% er sjeldan nødvendig eller praktisk
- Høg coverage garanterer ikkje kvalitet, men låg coverage er eit dårleg teikn
# calculator.py
def add(a, b):
"""Legger sammen to tall."""
return a + b
def divide(a, b):
"""Deler a på b."""
if b == 0:
raise ValueError("Kan ikke dele på null")
return a / b
def is_even(n):
"""Sjekker om et tall er partall."""
return n % 2 == 0Testar med unittest:
# test_calculator.py
import unittest
from calculator import add, divide, is_even
class TestCalculator(unittest.TestCase):
def test_add_positive_numbers(self):
self.assertEqual(add(2, 3), 5)
def test_add_negative_numbers(self):
self.assertEqual(add(-2, -3), -5)
def test_divide_normal(self):
self.assertEqual(divide(10, 2), 5)
def test_divide_by_zero(self):
with self.assertRaises(ValueError):
divide(10, 0)
def test_is_even_true(self):
self.assertTrue(is_even(4))
def test_is_even_false(self):
self.assertFalse(is_even(5))
def test_is_even_zero(self):
self.assertTrue(is_even(0))
if __name__ == '__main__':
unittest.main()Køyre testane:
python test_calculator.py -vOutput:
test_add_negative_numbers (__main__.TestCalculator) ... ok
test_add_positive_numbers (__main__.TestCalculator) ... ok
test_divide_by_zero (__main__.TestCalculator) ... ok
test_divide_normal (__main__.TestCalculator) ... ok
test_is_even_false (__main__.TestCalculator) ... ok
test_is_even_true (__main__.TestCalculator) ... ok
test_is_even_zero (__main__.TestCalculator) ... ok
----------------------------------------------------------------------
Ran 7 tests in 0.001s
OKPytest er eit populært alternativ med enklare syntaks:
# test_calculator_pytest.py
import pytest
from calculator import add, divide, is_even
def test_add_positive():
assert add(2, 3) == 5
def test_add_negative():
assert add(-2, -3) == -5
def test_divide_normal():
assert divide(10, 2) == 5
def test_divide_by_zero():
with pytest.raises(ValueError, match="Kan ikke dele på null"):
divide(10, 0)
def test_is_even():
assert is_even(4) == True
assert is_even(5) == False
assert is_even(0) == True
# Parametriserte tester - kjører samme test med forskjellige input
@pytest.mark.parametrize("a,b,expected", [
(2, 3, 5),
(0, 0, 0),
(-1, 1, 0),
(100, 200, 300),
])
def test_add_parametrized(a, b, expected):
assert add(a, b) == expectedKøyre pytest:
pytest test_calculator_pytest.py -vFordelar med pytest:
- Enklare syntaks (berre assert, ikkje self.assertEqual)
- Betre feilmeldingar
- Parametriserte testar
- Fixtures for oppsett/nedriving
validate_email(email) som returnerer True hvis e-post er gyldig.Steg 1 (Red) – Skriv test som feilar:
# test_email.py
from email_validator import validate_email
def test_valid_email():
assert validate_email("bruker@example.com") == True
def test_missing_at_sign():
assert validate_email("brukerexample.com") == False
def test_missing_domain():
assert validate_email("bruker@") == FalseKøyrer testane → FEIL (funksjonen finst ikkje)
Steg 2 (Green) – Skriv minimal kode:
# email_validator.py
def validate_email(email):
if '@' not in email:
return False
if email.endswith('@'):
return False
return TrueKøyrer testane → OK
Steg 3 (Refactor) – Forbetre koden:
# email_validator.py
def validate_email(email):
"""Validerer e-postadresse."""
if '@' not in email:
return False
parts = email.split('@')
if len(parts) != 2:
return False
local, domain = parts
if not local or not domain:
return False
if '.' not in domain:
return False
return TrueSteg 4 – Legg til fleire testar:
def test_no_dot_in_domain():
assert validate_email("bruker@example") == False
def test_multiple_at_signs():
assert validate_email("bru@ker@example.com") == FalseKøyrer testane → Oppdagar at multiple_at_signs feilar → Forbetre koden → Gjenta
Kva er hovudformålet med einingstestar?
A) Teste heile systemet frå perspektivet til brukaren
B) Teste éin enkelt funksjon isolert
C) Teste samspelet mellom database og backend
D) Teste tryggleik og yting
Oppgåve 2 (Flerval)
I kva for rekkjefølgje blir stega i Test-Driven Development (TDD) utførte?
A) Green → Red → Refactor
B) Refactor → Red → Green
C) Red → Green → Refactor
D) Red → Refactor → Green
Oppgåve 3
Forklar kort skilnaden mellom einingstestar og integrasjonstestar.
Oppgåve 4
Skriv ein unittest for denne funksjonen:
def is_palindrome(text):
"""Sjekker om en tekst er et palindrom."""
cleaned = text.lower().replace(" ", "")
return cleaned == cleaned[::-1]Test med minst tre forskjellige input (inkludert grensesituasjonar).
Oppgåve 5
Ein funksjon get_grade(score) skal returnere karakter basert på poengsum:
- 90-100: "A"
- 80-89: "B"
- 60-79: "C"
- 0-59: "F"
Skriv pytest-testar for denne funksjonen (inkludert grenseverdiar).
Oppgåve 6
Kva er code coverage, og kvifor er 100% coverage ikkje alltid eit meiningsfullt mål?
Oppgåve 7
Nemn tre ting du bør sjå etter når du gjer code review av ein pull request frå ein kollega.
Oppgåve 8
Forklar kort kva "Red-Green-Refactor"-syklusen i TDD tyder.
// --- Samleoppgåver ---
Samleoppgåve 1
Du har laga ei BankAccount-klasse med metodane deposit(), withdraw() og get_balance(). Withdraw skal kaste ein InsufficientFundsError hvis saldo er for låg.
a) Skriv komplette unittest-testar for alle tre metodar.
b) Inkluder minst éin test som verifiserer at rett exception blir kasta.
c) Legg til ein test for grensesituasjonen der withdraw prøver å ta ut nøyaktig saldoen.
Samleoppgåve 2
Eit team har 70% testdekningsgrad på ein Flask-webapplikasjon. Teamleiaren krev 95% coverage før lansering.
a) Forklar kvifor høg testdekningsgrad ikkje garanterer feilfri kode.
b) Kva for delar av ein webapplikasjon er mest kritiske å teste grundig?
c) Kva er skilnaden på einingstestar og end-to-end-testar for ein slik app?
d) Korleis kan teamet bruke TDD for ny funksjonalitet framover?
Oppsummering
I dette kapittelet har du lært:
- Testing: sikrar kvalitet i programvara.
- Einingstesting: testar enkeltdelar med unittest eller pytest.
- TDD: skrive test før kode.
- Kvalitetssikring: systematisk feilfinning.
- Automatisering: testar blir køyrde automatisk.
Nøkkelomgrep
| Omgrep | Forklaring |
|---|---|
| Einingstest | Test av ein enkelt kodedel |
| TDD | Testdriven utvikling (test før kode) |
| pytest | Rammeverk for testing i Python |
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.