# Enhetstest

## Vad det är

Ett enhetstest är en liten, automatisk kontroll av en enda kodbit för sig: det
anropar en funktion, en klass eller en liten modul med utvalda indata och
kontrollerar att det som kommer tillbaka är det som ska komma tillbaka. Det körs
i samma process som koden det testar, är klart nästan direkt och behöver varken
nätverk, databas eller webbläsare. Många svenska utvecklare säger helt enkelt
unit test, och arbetet med att skriva dem kallas enhetstestning.

Vad som räknas som en enhet är mindre givet än namnet antyder. I kapitlet om
unit testing i _Software Engineering at Google_ använder Erik Kuefler ordet om
tester med relativt snävt omfång, till exempel en enda klass eller metod, och
tillägger att en enhet kan vara så liten som en funktion eller så bred som en
grupp närliggande paket. Den gräns som går att använda i praktiken går där andra
anropar koden: en enhet är det som erbjuder ett gränssnitt som någon utanför är
beroende av, och dess tester använder gränssnittet på samma sätt som anroparna.

De moderna ramverken har ett gemensamt ursprung. Kent Becks artikel _Simple
Smalltalk Testing: With Patterns_ beskrev ett litet ramverk uppbyggt av
fixtures, testfall, kontroller och sviter, och rekommenderade att utvecklarna
skrev sina egna enhetstester, ett per klass. Pythons `unittest` skriver i sin
dokumentation att det ursprungligen inspirerades av JUnit, och det har samma
fyra begrepp: en test fixture (förberedelsen ett test behöver och städningen
efteråt), ett testfall (den enskilda testenheten), en testsvit och en
testkörare. Båda drar en gräns som är värd att hålla fast vid: en failure är en
kontroll som fick fel svar, medan en error är något testet inte hade räknat med
alls, till exempel ett undantag som kastas långt inne i koden.

Enhetstester ligger i den smala änden av en skala. I bokens kapitel om testning
sorterar Adam Bender tester efter omfång: smala tester kallas vanligen unit
tests, mellanstora tester som täcker några komponenter tillsammans är
integrationstester, och stora tester som driver hela systemet är
end-to-end-tester.

## Varför det spelar roll

Snabbhet är det första skälet. Ett enhetstest körs i samma process som koden
det testar, så en utvecklare kan köra tusentals av dem under en vanlig
arbetsdag, och ett misstag syns medan ändringen som orsakade det fortfarande
står på skärmen. Googles riktlinjer är raka om vad de tillåter: ett litet test
får inte sova, göra I/O eller röra nätverket eller disken. Det som blir kvar är
snabbt och deterministiskt, och ett team lär sig lita på en svit som ger samma
svar varje gång.

Det andra är felsökning. När ett smalt test fallerar sitter felet i en liten
kodbit, oftast den som just ändrades. När ett test som driver hela systemet
fallerar kan felet sitta var som helst mellan webbläsaren och databasen. Därför
beskriver _Software Engineering at Google_ en pyramid som en grov riktlinje:
ungefär 80 % smala tester, 15 % integrationstester och 5 % end-to-end-tester.

Det tredje är förändring. En svit som kontrollerar beteende genom ett publikt
gränssnitt gör det möjligt att bygga om koden bakom gränssnittet utan rädsla:
går testerna fortfarande igenom får anroparna fortfarande det de ska ha.
Kueflers ideal är ett test som aldrig behöver ändras om inte kraven ändras, så
att en ren refaktorering inte rör ett enda test.

Enhetstester dokumenterar också. Ett test som är uppkallat efter ett beteende,
med enkla indata och ett förväntat resultat utskrivet i sin helhet, visar nästa
läsare hur koden ska anropas och vad den lovar, och till skillnad från en
kommentar fallerar det så fort det slutar stämma.

## Så ser det ut i praktiken

Ta en funktion som läser varaktigheter som `1h30m` ur en konfigurationsfil och
räknar om dem till sekunder:

```python
# durations.py
UNITS = {"h": 3600, "m": 60, "s": 1}


def to_seconds(text: str) -> int:
    """Räkna om "1h30m" eller "45s" till ett helt antal sekunder."""
    total, digits = 0, ""
    for char in text:
        if char.isdigit():
            digits += char
        elif char in UNITS and digits:
            total += int(digits) * UNITS[char]
            digits = ""
        else:
            raise ValueError(f"not a duration: {text!r}")
    if digits or not text:
        raise ValueError(f"not a duration: {text!r}")
    return total
```

Funktionens enhetstester, skrivna för pytest, kontrollerar två beteenden: vad
den tar emot och vad den avvisar.

```python
# test_durations.py
import pytest

from durations import to_seconds


@pytest.mark.parametrize(
    "text,expected",
    [("45s", 45), ("2m", 120), ("1h30m", 5400), ("0s", 0)],
)
def test_counts_each_unit(text, expected):
    assert to_seconds(text) == expected


@pytest.mark.parametrize("text", ["", "90", "h", "1x", "1.5h"])
def test_refuses_what_it_cannot_read(text):
    with pytest.raises(ValueError):
        to_seconds(text)
```

Kör man `pytest` i katalogen samlar det in alla filer som heter `test_*.py`
eller `*_test.py`, och de två funktionerna vecklas ut till nio fall, ett per rad
med parametrar. Dokumentationen till pytest beskriver varje test som arrange,
act, assert och cleanup; här är förberedelsen parameterraden, handlingen är det
enda anropet, och det finns inget att städa upp eftersom inget utanför
funktionen berörs. Råkar någon senare sätta minuten till `3600` sekunder
fallerar två testfall, och rapporten namnger vart och ett efter dess parametrar:
`test_counts_each_unit[2m-120]` visar `7200` mot förväntade `120`, och
`test_counts_each_unit[1h30m-5400]` visar `111600` mot `5400`. Lägg märke till
att de förväntade värdena är uträknade för hand, inte kopierade från det koden
returnerade första gången.

Samma kontroller i standardbibliotekets `unittest` skulle bli en underklass till
`unittest.TestCase` med metoder vars namn börjar på `test`, och med
`assertEqual` och `assertRaises`; `python -m unittest` hittar som standard filer
som matchar `test*.py`. Go har byggt in konventionen i verktygskedjan: en
funktion `func TestXxx(t *testing.T)` i en fil vars namn slutar på `_test.go`
körs av `go test`.

Skalan ändrar bokföringen, inte idén.
[Databasgeneratorn med 981 testfall](/sv/portfolio/held-the-generator-to-981-tests-and-92-coverage/),
ett arbete utfört i juli 2026, räknar 383 testfunktioner som blir 981 fall när
de parametriserade har expanderats, fördelade på 45 filer och 7 944 rader.
Bygget upprätthåller ett golv på 92 % grentäckning, sviten ligger på ungefär
95 %, och varningar behandlas som fel. Sidan är ärlig om hur långt talet räcker:
två av de fel som hittades senare i projektet låg i kod som täckningsrapporten
visade som täckt.

## Var det går fel

**Testerna speglar implementationen.** Ett test som griper in i privata
hjälpfunktioner eller kontrollerar den exakta ordningen av interna anrop går
sönder varje gång koden byggs om, trots att inget en anropare kan se har
ändrats. Kuefler kallar det ett skört test och pekar ut överdriven användning
av mockningsramverk som den vanligaste orsaken till att tester av interaktioner
går snett. Testa genom det publika gränssnittet, kontrollera det tillstånd som
anropet lämnar efter sig snarare än anropen som ledde dit, och använd riktiga
objekt där de är billiga.

**Testerna är beroende av varandra.** Ett test skapar en post, nästa läser den,
och sviten går igenom bara i den ordningen. Kör ett enda test, eller samma
tester i en annan ordning, och det fallerar av ett skäl som ingen läsare kan se.
Becks ramverk skapade sina testdata och kastade dem igen för varje test, och
dokumentationen för `unittest` ber att ett testfall ska vara helt
självständigt. Ge varje test en egen fixture och städa efter det.

**Täckning blir målet.** Täckning visar att en rad kördes, inte att något
kontrollerade vad den gjorde. Ett test utan en enda assertion höjer siffran och
bevisar ingenting, och _Software Engineering at Google_ varnar för att ett krav
på 80 % brukar bli ett tak, så att ändringar landar på exakt miniminivån.
Behandla siffran som ett golv, och läs assertions under kodgranskningen.

**Ett test per metod.** Att ge varje metod i produktionskoden en egen
testmetod ser prydligt ut, men testet växer varje gång metoden får en ny gren,
tills ingen kan säga vad det kontrollerar. Kueflers råd är att testa beteenden
i stället: ett beteende per test, uppkallat efter det det bevisar, så att en
felrapport kan läsas som en mening om vad som gick sönder.

**Testet väntar på att tiden ska gå.** En `sleep()` som ska ge något tid att
bli klart gör sviten långsammare vid varje körning och fallerar ändå på en
belastad maskin. I ett enhetstest är tiden indata: ge koden en klocka eller en
tidsstämpel, så att testet själv kan ställa in den tidpunkt det behöver i
stället för att vänta på den.

## Läs mer

- [unittest: Unit testing framework](https://docs.python.org/3/library/unittest.html)
  — ramverket i Pythons standardbibliotek: fixtures, testfall, sviter,
  testkörare, testupptäckt och skillnaden mellan en failure och en error.
- [Anatomy of a test](https://docs.pytest.org/en/stable/explanation/anatomy.html)
  — pytests förklaring av arrange, act, assert och cleanup.
- [Get Started](https://docs.pytest.org/en/stable/getting-started.html)
  — pytests egen introduktion: vanlig `assert`, testupptäckt och
  `pytest.raises`.
- [Unit Testing](https://abseil.io/resources/swe-book/html/ch12.html)
  — Erik Kueflers kapitel i _Software Engineering at Google_ om sköra tester,
  publika API:er, tillstånd framför interaktioner och testning av beteenden.
- [Simple Smalltalk Testing: With Patterns](https://web.archive.org/web/20150315073817/http://www.xprogramming.com/testfram.htm)
  — Kent Becks artikel om ramverket, om att skriva enhetstester ett per klass
  och om failures och errors, i en arkiverad kopia.
- [testing](https://pkg.go.dev/testing)
  — Gos standardpaket för testning: `TestXxx`-funktioner, `_test.go`-filer och
  `go test`.

<https://engineer.company/sv/glossary/unit-test/>
