E2E-testing: Playwright og smarte agenter er en vinnende kombinasjon

Det hele startet som et sideprosjekt én helg. Jeg ville bare se om det var mulig å få én brukerflyt til å kjøre gjennom automatisk - fra "opprett sak" til "fatt vedtak". Med Claude Code som AI-assistent fikk jeg det til å fungere, og plutselig hadde vi noe som faktisk var nyttig.

Oppsummert

Tidsperiode
Tjenesteområde
Verktøy og metodikk

Melosys er NAVs system for håndtering av medlemskap i folketrygden og trygdeavgiftsberegning for personer som jobber på tvers av landegrenser. Systemet består av over 17 mikrotjenester, en Oracle-database, Kafka-meldingskøer og kompleks forretningslogikk. Ikke akkurat det enkleste systemet å E2E-teste.

Tre måneder og kanskje to ukers effektiv arbeidstid senere har vi 54 tester, et fullverdig CI/CD-oppsett med automatiske triggere, og har oppdaget 7 produksjonsfeil. Denne artikkelen tar deg med på reisen.

Grunnlaget legges

Målet var ambisiøst: automatisere komplekse brukerflyter fra "opprett sak" til "fatt vedtak" i et system med mange avhengigheter. Vi startet med det mest grunnleggende - å få alle tjenestene til å kjøre sammen.

Docker Compose: 17 tjenester i harmoni

Den største tekniske utfordringen var å få alle tjenestene til å snakke sammen. Melosys-økosystemet består av:

Frontend og backend:

  • melosys-web (React/Nginx)
  • melosys-api (Spring Boot hovedapplikasjon)
  • melosys-eessi (EU-integrasjon)
  • faktureringskomponenten, melosys-dokgen, melosys-trygdeavgift-beregning, og flere

Databaser og infrastruktur:

  • Oracle database (hoveddata)
  • PostgreSQL (støttetjenester)
  • Kafka (meldingskø)
  • Unleash (feature toggles)

Autentisering og mocks:

  • mock-oauth2-server (autentisering)
  • melosys-mock (alle eksterne tjenester)

En kritisk designbeslutning var å bruke et eksternt Docker-nettverk som alle tjenestene kobler seg til. Dette gjør at tjenestene kan finne hverandre via interne DNS-navn, akkurat som i produksjon.

Kontinuerlig integrasjon: Dispatch-systemet

En av de viktigste delene av infrastrukturen er dispatch-systemet som automatisk kjører E2E-tester når noen pusher kode.

Illustrasjon: Google Nano Banana  

Hvordan det fungerer

Når en utvikler merger til main i for eksempel melosys-api, skjer følgende:

  1. Build og push : GitHub Actions bygger en ny Docker-image og pusher den til Google Artifact Registry (GAR) med tag  latest
  2. Dispatch-trigger : Etter vellykket push sender workflowen en  repository_dispatch event til E2E-repoet
  3. E2E-kjøring : E2E-testene starter automatisk med den nye imagen
  4. Resultat : Utvikleren får tilbakemelding om imagen er stabil eller ikke

Vi lytter på dispatch-events fra ni forskjellige repositories:

  • melosys-api
  • melosys-web
  • faktureringskomponenten
  • melosys-eessi
  • melosys-trygdeavgift-beregning
  • melosys-trygdeavtale
  • melosys-inngangsvilkar
  • melosys-dokgen
  • melosys-mock

Image-tagging: Samme miljø overalt

En viktig del av strategien vår er hvordan vi tagger Docker-images. Når kode merges til main, bygges en image og tagges som latest . Denne latest -tagen brukes i:

  • Dev-miljøet i skyen  (q1/q2)
  • E2E-testene i GitHub Actions
  • Lokal utvikling

Dette betyr at utviklere lokalt kan kjøre nøyaktig de samme image-versjonene som kjører i test- og utviklingsmiljøene. Ingen "det fungerer på min maskin"-problemer.

Fleksibel manuell kjøring

I tillegg til automatiske triggere kan vi kjøre testene manuelt med flere nyttige parametere:

  • environment : Spesifiser image-tags (f.eks.  melosys-api:fix-bug,melosys-web:latest )
  • test_grep : Filtrer hvilke tester som kjøres (f.eks. bare "EU/EØS"-tester)
  • repeat_each : Kjør hver test N ganger (for å finne flaky tester)
  • disable_retries : Slå av retries for nøyaktig måling av feilrate

Muligheten til å spesifisere ulike tags for ulike tjenester er kritisk for debugging. Hvis vi mistenker at en feil ligger i melosys-web, kan vi kjøre med melosys-web:fix-attempt-1 mens alle andre tjenester bruker latest .

AI-drevet debugging med Claude Code

Den virkelig spennende delen av prosjektet er hvordan vi bruker Claude Code til å automatisk debugge og fikse feil i systemet.

Illustrasjon: Google Nano Banana

Debugging-løkken

Når en E2E-test feiler i CI, kan Claude Code analysere feilen og forsøke å fikse den automatisk. Prosessen ser slik ut:

Steg 1: Analyser feilen

Claude Code har en egen skill ( gh-test-results ) som laster ned og analyserer testresultater fra GitHub Actions. Den henter:

  • Testrapporter med feilmeldinger
  • Docker-logger fra alle tjenester
  • Skjermbilder og traces fra feiltidspunktet

Steg 2: Identifiser rotårsaken

Basert på analysen kan Claude Code identifisere om feilen ligger i:

  • Frontend (melosys-web) - for eksempel race conditions i UI
  • Backend (melosys-api) - for eksempel manglende API-endepunkt eller feil logikk
  • Mock-tjenesten - manglende testdata
  • Selve testen - feil antagelser eller timing-problemer

Steg 3: Implementer fix

Claude Code kan deretter navigere til riktig repository (melosys-api, melosys-web, osv.) og implementere en fix. Dette kan være alt fra å legge til en API-venting i frontend til å fikse en race condition i backend.

Steg 4: Bygg og push med unik tag

Med en annen skill ( docker-build-push ) bygger Claude Code en ny Docker-image lokalt og pusher den til GAR. Viktig her er at vi bruker en unik tag - ikke latest - slik at vi kan teste fixen uten å påvirke andre. For eksempel melosys-web:fix-arbeidsgiver-v1

Steg 5: Test fixen

Claude Code trigger en ny E2E-kjøring med:

  • Den nye image-tagen (f.eks.  melosys-web:fix-arbeidsgiver-v1 )
  • Andre tjenester på  latest
  • Bare den berørte testen ( test_grep )
  • Flere iterasjoner ( repeat_each: 10 ) for å verifisere stabilitet

Steg 6: Iterer med nye versjoner

Hvis testen fortsatt feiler, analyserer Claude Code de nye resultatene og prøver en annen tilnærming. Den bygger en ny versjon ( fix-arbeidsgiver-v2 ), pusher, og tester igjen. Denne løkken kan fortsette til en stabil løsning er funnet.

Flerrepo-debugging

Noen ganger krever en fix endringer i flere repositories samtidig. Kanskje frontend trenger en ny venting, og backend trenger å returnere data raskere. Da kan vi:

  1. Fikse i melosys-web → bygge og pushe  melosys-web:fix-timing-v1
  2. Fikse i melosys-api → bygge og pushe  melosys-api:fix-timing-v1
  3. Kjøre E2E med begge:  melosys-api:fix-timing-v1,melosys-web:fix-timing-v1

Dette gir oss muligheten til å teste kombinasjoner av endringer på tvers av repositories før vi merger noe.

Et praktisk eksempel

La oss si at EU/EØS-testen feiler sporadisk med en timeout på arbeidsgiver-valg. Claude Code vil:

  1. Laste ned testresultatene og Docker-loggene
  2. Se at melosys-api returnerer arbeidsgiverlisten, men frontend prøver å klikke før listen er rendret
  3. Gå til melosys-web og legge til en eksplisitt venting på at listen er synlig
  4. Bygge ny image:  melosys-web:fix-arbeidsgiver-v1
  5. Kjøre E2E 10 ganger:  environment=melosys-web:fix-arbeidsgiver-v1, test_grep=EU/EØS, repeat_each=10
  6. Hvis 8/10 passerer: analysere på nytt, prøve  fix-arbeidsgiver-v2
  7. Hvis 10/10 passerer: foreslå å merge fixen til main

Denne iterative prosessen har dramatisk redusert tiden vi bruker på å debugge flaky tester.

Stabilisering: De kritiske valgene

Én worker - den magiske innstillingen

En av de viktigste beslutningene for stabilitet var å kjøre testene sekvensielt med kun én worker. Hvorfor?

  1. Database-isolasjon : Med parallelle tester ville testene tråkke på hverandre i databasen
  2. Docker-logger : Med én worker kan vi presist fange logger per test basert på tidsstempler
  3. Forutsigbarhet : Sekvensiell kjøring gjør det lettere å reprodusere feil

Health checks: Vente på Oracle

Oracle-databasen tar tid å starte opp. Uten eksplisitt venting ville testene starte før databasen var klar. I GitHub Actions venter vi på at alle kritiske tjenester er friske:

if [ "$oracle_health" = "healthy" ] &&
[ "$kafka_health" = "healthy" ] &&
[ "$api_health" = "healthy" ]; then
echo "✅ All critical services are healthy!"
fi

Automatisk opprydding

Illustrasjon: Google Nano Banana Vi bygde fixtures som automatisk rydder opp før og etter hver test:


Før hver test:

  • Tøm database (TRUNCATE for hastighet)
  • Slett mock-data
  • Reset feature toggles til standardverdier
  • Vent på cache-propagering

Etter hver test:

  • Vent på at asynkrone prosesser fullføres
  • Reset feature toggles for neste test
  • Sjekk Docker-logger for feil

Docker-loggmonitorering

Hver test logger start- og sluttid. Etter testen sjekker vi Docker-loggene fra alle tjenester for feil som oppsto i det tidsvinduet. Feil kategoriseres som SQL-feil, connection-feil eller andre feil, og legges ved i testrapporten.

Page Object Model: Fra kaos til struktur

I starten brukte vi inline-selektorer direkte fra Playwright Codegen. Dette ble raskt uvedlikeholdbart. Vi migrerte til Page Object Model (POM) der hver side har sin egen klasse med metoder som fyllInnBrukerID() og velgSakstype() .

Testene ble mye mer lesbare. I stedet for kryptiske selektorer som getByRole('combobox', { name: 'Velg land' }) kan vi skrive behandling.velgLand('Danmark') .

Race conditions: Den store fienden

Gjennom prosjektet møtte vi mange race conditions. Den mest komplekse var knyttet til saga-mønsteret i backend.

Saga-mønsteret og frontend-konflikter

Melosys-api bruker et saga-mønster for å orkestrere flerstegsprosesser asynkront. Når en saksbehandler fatter et vedtak, starter en saga som går gjennom steg i en egen tråd - for eksempel å sende brev, oppdatere registre, og generere meldinger.

Problemet er at frontend og backend ikke er synkronisert. Frontend vet ikke at sagaen kjører, og sender mange parallelle API-kall mens backend er opptatt med å prosessere. Dette skaper konflikter - flere transaksjoner prøver å oppdatere samme data samtidig.

Dette er et symptom på at det var mer frontend-kompetanse enn backend-kompetanse da løsningen ble laget. Mye logikk ble lagt i frontend som egentlig burde vært i backend. E2E-testene avdekket dette mønsteret konsekvent, mens manuell testing bare sporadisk traff timingen.

7 produksjonsfeil oppdaget

Etter at vi fikk E2E-testene til å kjøre stabilt med Docker-loggmonitorering, oppdaget vi 7 produksjonsfeil som vi kunne fikse. Kombinasjonen av Playwright-traces og Docker-logger fra melosys-api ga oss innsikt vi aldri hadde fått ellers. Vi kunne se nøyaktig hva frontend gjorde, og samtidig hva som skjedde i backend - inkludert feil som bare logges og aldri vises til bruker.

Andre race conditions

Dynamiske dropdowns : Mange dropdowns lastes basert på tidligere valg. Hvis vi prøver å velge en verdi før listen er populert, feiler testen.

Feature toggle-propagering : Når vi endrer en toggle, tar det tid før melosys-api henter den nye verdien.

Asynkrone prosessinstanser : Vi bygde et eget endpoint i melosys-api som venter på at alle prosessinstanser er ferdige før cleanup.

Resultater og statistikk

Utviklingstiden på bare 2 uker hadde ikke vært mulig uten Claude Code. AI-en navigerte seg gjennom 17 tjenester, fant race conditions i Docker-logger, og itererte på fiks til de var stabile.

Testene våre dekker FTRL-saker, EU/EØS lovvalg, trygdeavtalesaker, journalføring, oppgavebehandling, søk, SED-mottak, klagebehandling og årsavregning.

Lærdommer

Hva vi ville gjort annerledes

Vi burde startet tidligere. Det er mye vanskeligere å sette opp E2E-tester sent i et prosjekt. Systemet har vokst, avhengighetene er mange, og race conditions har fått tid til å manifestere seg i produksjon.

Men hadde vi prøvd dette med Selenium for noen år siden, hadde det ikke fungert like bra. Playwright sin auto-wait, trace-viewer, og moderne arkitektur gjør en enorm forskjell. Kombinert med AI-assistert debugging blir det faktisk gjennomførbart å teste et system med 17 tjenester.

Hva som fungerte godt

  1. Dispatch-systemet : Automatisk testing ved hver push gir trygghet
  2. Én worker : Sekvensiell kjøring med Docker-loggmonitorering var uvurderlig
  3. Claude Code debugging-løkken : AI-assistert iterasjon på feilretting spart utallige timer
  4. Trace og video på alle tester : Selv vellykkede tester har sporingsdata

Claude Codes rolle

Claude Code var avgjørende for hele prosjektet:

  • Analysere og forstå det komplekse Melosys-økosystemet
  • Foreslå arkitektoniske mønstre (fixtures, POM)
  • Debugge race conditions ved å lese Docker-logger
  • Automatisk iterere på fiks til de var stabile
  • Dokumentere prosjektet

Oppsummering

Å bygge en stabil E2E-testinfrastruktur for et komplekst distribuert system er utfordrende, men absolutt oppnåelig. Nøkkelprinsippene er:

  • Isolasjon : Hver test må starte med ren tilstand
  • Observerbarhet : Logger og traces er dine beste venner
  • Iterasjon : Bruk AI til å raskt teste og iterere på løsninger
  • Automatisering : Dispatch-triggere sikrer at hver endring testes
  • Pragmatisme : Én worker er bedre enn flaky parallelle tester

Med riktig infrastruktur, automatiske triggere og AI-assistert debugging kan E2E-tester være en verdifull del av utviklingsprosessen, ikke en kilde til frustrasjon.

Ressurser

 

Flere historier

Text Link
Text Link
Text Link

Man må starte et sted…

Vil du skape en organisasjon der du både leverer og trives? La oss ta en prat.

Oliver

obr@capraconsulting.no
+47 971 20 556

Bli kontaktet av oss

Takk!
Vi følger deg opp innen kort tid!
Oops! Something went wrong while submitting the form.