Testing

Når du bygger tjenester for kunder, er testing en del av kvalitetssikringen og ikke en ettertanke. Testene skal ikke bare bevise at koden fungerer i dag, men gjøre det tryggere å endre, refaktorere og utvide koden senere uten å miste kontroll.

I praksis betyr det at gode tester hjelper deg med tre ting samtidig: de fanger feil tidlig, de dokumenterer forventet oppførsel og de gjør systemet enklere å vedlikeholde når flere personer eller flere leveranser er involvert.

1) Testformat og kontrakt

Bruk korte testblokker med ett tydelig ansvar. En god test skal kunne leses som en liten kontrakt: dette er situasjonen, dette er handlingen og dette er det forventede utfallet.

test "enkel addisjon" {
    la total: heltall = 2 + 3
    assert_eq(total, 5)
}

Når testene er korte og presise, blir de også mer nyttige som dokumentasjon. Da er det lettere å forstå hva en funksjon egentlig er ment å gjøre uten å lese all produksjonskoden først.

2) Arrange-Act-Assert

Arrange-Act-Assert er et enkelt mønster som hjelper testene til å holde struktur. Først setter du opp dataene, deretter kjører du handlingen, og til slutt sammenligner du resultatet med det du forventer.

funksjon beregn_pris(grunn: heltall, tillegg: heltall) -> heltall {
    returner grunn + tillegg
}

test "beregn_pris" {
    // Arrange
    la grunn: heltall = 1000
    la tillegg: heltall = 250

    // Act
    la total: heltall = beregn_pris(grunn, tillegg)

    // Assert
    assert_eq(total, 1250)
}

Dette mønsteret er spesielt nyttig når domenelogikken begynner å vokse. Det hjelper deg å unngå tester som gjør mange ting på en gang og som derfor blir vanskeligere å stole på når de feiler.

3) Test av web-håndterere

For webkode er det ofte nyttig å teste handlerne direkte, så langt det lar seg gjøre. Da kan du bekrefte at ruter, status, felt og struktur i responsen holder seg stabile uten å måtte kjøre et helt separat miljø rundt hver test.

test "status-endepunkt svarer" {
    la respons: tekst = status_handler("GET", "/api/status", "", "")
    assert(tekst_inneholder(respons, "\"ok\":true"))
    assert(tekst_inneholder(respons, "\"service\":\"norscode\""))
}

Det viktigste her er ikke at responsen bare eksisterer, men at den har meningsfullt innhold. Hvis testen bare sjekker at noe kom tilbake, blir den fort for svak til å fange reelle regresjoner.

4) Test av gyldig og ugyldig input

Mange feil oppstår ikke når alt går bra, men når input mangler, er tom, kommer i feil metode eller har en struktur du ikke forventet. Derfor bør du bevisst teste både godkjente og avviste scenarier.

funksjon kontakt_handler(method: tekst, path: tekst, query: tekst, body: tekst) -> tekst {
    hvis method != "POST" da {
        returner "{\"error\":\"Kun POST\"}"
    }
    la navn: tekst = web.http_body_json_felt_tekst(body, "navn")
    hvis navn == "" da {
        returner "{\"error\":\"navn mangler\"}"
    }
    returner "{\"ok\":true}"
}

test "kontakt validering" {
    la bad: tekst = kontakt_handler("POST", "/api/kontakt", "", "{\"navn\":\"\"}")
    assert(tekst_inneholder(bad, "navn mangler"))
    
    la good: tekst = kontakt_handler("POST", "/api/kontakt", "", "{\"navn\":\"Ada\"}")
    assert(tekst_inneholder(good, "\"ok\":true"))
}

Denne typen test er ofte viktigere enn den ser ut. Når du eksplisitt definerer hva som skal avvises, reduserer du rommet for uklare feilbaner senere i systemet.

5) Teste OpenAPI-kontrakt

Hvis du publiserer en OpenAPI-oversikt eller annen maskinlesbar kontrakt, er det lurt å teste at de viktigste rutene faktisk finnes der. Det gjør dokumentasjonen mindre skjør og hjelper deg å oppdage utilsiktede brudd mellom kode og grensesnitt.

test "openapi har viktige ruter" {
    la api: tekst = api_openapi()
    assert(tekst_inneholder(api, "\"/api/status\""))
    assert(tekst_inneholder(api, "\"/api/kontakt\""))
}

6) Testkommandoer

bin/nc check nettside.no
bin/nc test projects/language/website/tests/min_test.no

check validerer koden. test kjører testfilene.

I en liten kodebase kan disse to kommandoene dekke mye av det daglige behovet. Poenget er å bruke dem ofte nok til at feil oppdages mens endringen fortsatt er fersk og enkel å rette.

7) Vanlige testmønstre

Et godt testsett inneholder gjerne en blanding av små, raske enhetstester og noen litt bredere tester av ruter eller kontrakter. Da får du både presisjon og bedre beskyttelse mot at flere deler plutselig glir fra hverandre.

8) Vanlige feil

  • For mange påstander i én test
  • Blindt sjekke lengde(respons) > 0 uten semantikk
  • Ikke skille gyldig og ugyldig input
  • Teste sideeffekter uten observerbar output

En annen vanlig feil er å skrive tester som er så tett knyttet til intern implementasjon at de brekker hver gang koden ryddes opp, selv om adferden mot brukeren egentlig er den samme. Da mister testene noe av verdien sin som sikkerhetsnett.

9) Eget testprogram

Lag en testfil tests/kjerne.no med denne strukturen:

test "kjerne sanity" {
    assert_eq(tekst_fra_heltall(2 + 2), "4")
}

Dette er et enkelt startpunkt, men poenget er å utvide derfra med tester som speiler reell forretningslogikk, reelle responser og reelle valideringsregler i prosjektet ditt.

10) Neste steg

Bruk dette mønsteret i ditt første forretningsområde, og koble deretter ruter i web-dokumentasjonen. Hvis prosjektet ditt begynner å vokse, er neste nyttige steg ofte å se testing i sammenheng med moduler og prosjektstruktur, slik at testene følger ansvarsdelingen i koden i stedet for å bli liggende som