Gå til hovedinnhold

Hvordan sikrer vi kvalitet?

Dette dokumentet forklarer hvordan vi tenker rundt kvalitet og testing i Studieadministrasjon. Her beskriver vi både filosofien vår og den praktiske virkeligheten - inkludert hvor vi er på vei.

Hvorfor kvalitet gjør oss raskere

Det er en vanlig misforståelse at kvalitet og hastighet står i motsetning til hverandre. Data fra DORA (DevOps Research and Assessment) viser det motsatte: Organisasjoner som leverer raskere har også lavere feilrate.

Performance levelChange lead timeDeployment frequencyChange fail rateFailed deployment recovery time
Elite< 1 dagOn demand (flere per dag)5%< 1 time
High1 dag - 1 uke1/dag - 1/uke20%< 1 dag
Medium1 uke - 1 måned1/uke - 1/måned10%< 1 dag
Low1-6 måneder1/måned - 1/6 mnd40%1 uke - 1 måned

Kilde: DORA Accelerate State of DevOps 2024

Om tallene

Medium-gruppen har lavere feilrate (10%) enn High-gruppen (20%), men også lavere throughput (tregere deployment og lengre lead time). DORA valgte å kalle de raskere teamene "high performers" til tross for høyere feilrate, fordi rask tilbakemelding er verdifullt. Poenget er ikke å nå et bestemt nivå, men å forbedre seg kontinuerlig.

Nøkkelen er automatisering og gode tilbakemeldingsløkker. Vi påstår derfor at vi kan levere oftere og med høyere kvalitet ved å investere i automatisering, testing og observerbarhet.

Hva er kvalitet?

Kvalitet har to dimensjoner:

Det vi lager fungerer

  • Teknisk korrekthet: Koden gjør det den skal uten feil
  • Ytelse: Systemet responderer raskt og håndterer last
  • Stabilitet: Tjenestene er tilgjengelige når brukerne trenger dem
  • Sikkerhet: Data og systemer er beskyttet

Det vi lager er det brukeren vil ha

  • Løser riktig problem: Vi bygger det som faktisk trengs
  • God brukeropplevelse: Intuitivt og effektivt å bruke
  • Tilbakemeldingsløkker: Vi lærer kontinuerlig fra brukerne

Begge dimensjoner er nødvendige. En teknisk perfekt løsning som løser feil problem er ikke kvalitet. En løsning som løser riktig problem, men krasjer hele tiden, er heller ikke kvalitet.

Vår tilnærming: Kontinuerlige leveranser

Vårt mål er kontinuerlig produktutvikling: Tiden fra en utvikler plukker en oppgave fra backloggen til vi har utført de nødvendige endringene i produksjon skal være så kort som mulig.

Nåsituasjon sett opp mot målbilde

Målbildet vårt er trunk-based development hvor:

  • Vi merger ofte til main (minst daglig)
  • Endringer deployes automatisk hele veien til produksjon uten manuelle steg
  • Vi har høy testdekning som gir oss trygghet
  • Feature flags skjuler uferdige funksjoner i produksjon
  • Tidlig tilgang til funksjonalitet under utvikling sikrer god brukermedvirkning

Hvor vi er i dag:

  • Vi følger trunk-based prinsipper med merge til main som release-kandidat for backend
  • Frontend bruker versjonering med manuelle releases
  • Noen applikasjoner har automatisk deployment til produksjon, andre krever manuelle steg
  • Vi jobber med å øke testdekning og implementere feature flags
  • Vi bygger verktøy (studieadm-cli, adhoc-miljøer) for å støtte raskere feedback

Dette er en bevisst reise. Vi prioriterer å bygge riktig fundament fremfor å ta snarveier.

Hva tester vi når?

Utviklere må skrive automatiserte tester som del av normal utvikling. Dette er en kritisk forutsetning for å kunne levere kontinuerlig. Manuell/utforskende testing skjer derfor etter at funksjonaliteten har blitt levert.

Automatiserte tester

EnhetstesterIntegrasjonstesterEnde-til-ende
ScopeEnkeltfunksjoner isolertKomponenter sammenHele brukerreiser
NårSamtidig med kodeFor interaksjon mellom lagFør deploy
Hvor kjørerLokalt + CILokalt + CICD*
AnsvarUtviklerUtviklerUklart
EksempelBeregn karaktersnitt med edge casesService → database → JSONInnlogging → søk → påmelding
VerdiRask feedback, levende dokumentasjonAvdekker integrasjonsfeilVerifiserer hele systemet

* Målbilde - E2E-tester eksisterer men kjører ikke automatisk i CD-pipeline ennå

Enhetstester er fundamentet. Disse skrives samtidig med koden og gir øyeblikkelig tilbakemelding når noe går galt. En god enhetstest er så rask at du kan kjøre hundrevis på sekunder, og så presis at du umiddelbart ser hva som feilet. Tenk på disse som sikkerhetsnett som fanger logikkfeil før de når andre deler av systemet.

Integrasjonstester verifiserer at delene fungerer sammen. Disse fanger opp feil som oppstår når komponenter møtes - database-lag, API-kall, meldingskøer. Fordi de er langt tregere enn enhetstester, er det vanlig å hoppe over dem under aktiv utvikling. Før du lager merge request bør du kjøre integrasjonstestene lokalt for å bekrefte at alt henger sammen. CI-pipelines kjører alltid alle integrasjonstestene med egne testdatabaser og mock-services, slik at samtidige pipelines ikke påvirker hverandre.

Ende-til-ende tester sikrer at alt fungerer fra brukerens perspektiv. Disse tester fullstendige brukerreiser på tvers av alle tjenestene våre. Fordi de er trege og krever at hele systemet kjører, har vi færre av disse, men de er kritiske for å fange feil som bare oppstår når alt jobber sammen.

Status: E2E-testing

Vi har E2E-tester for noen kritiske brukerreiser, men disse kjører ikke automatisk i deployment-pipelinen ennå. Vi jobber med å sette opp isolerte CD-miljøer hvor testene kan kjøre som del av automated deployment.

Utforskende testing

Automatiserte tester er kritiske, men de kan bare finne feil vi har tenkt på å teste for. Utforskende testing er når vi aktivt leter etter problemer - bruker systemet som en faktisk bruker ville gjort, prøver rare kombinasjoner, og utforsker edge cases vi ikke har automatisert ennå.

Dette gjøres kontinuerlig, spesielt når nye features lanseres. Bruk testmiljøet eller adhoc-miljøer, ofte med feature flags for å teste funksjoner før de er klare for alle. Alle i teamet - utviklere, testere, produkteiere - må bidra til utforskende testing fordi ulike perspektiver fanger ulike problemer.

UX-review før merge

I dag skjer det testing av endringer i FS Admin manuelt, som del av code review før merge til main. Revieweren sjekker da ut endringene i review-miljøet, verifiserer funksjonaliteten, og godkjenner merge først etter at endringen er testet. Dette er et kvalitetssteg man har innført en del steder nylig som følge av mangelen på mellom annet automatisert ende-til-ende testing.

Målbildet: Når automatisert testdekning forbedres vil behovet for manuell akseptansetesting reduseres. Code review kan da fokusere på kodekvalitet, arkitektur og design fremfor manuell funksjonalitetstesting.

Visualisering av testflyten

Diagrammet under viser målbildet for hvordan en kodeendring skal gå fra utvikling til produksjon. Det fokuserer på utviklerperspektivet - hva gjør du, og når kjører testene?

Feature flags gjør trygg utrulling mulig

Merk at vi deployer til produksjon før utforskende testing er ferdig. Dette er trygt fordi nye funksjoner skjules bak feature flags.

Med feature flags kan vi:

  • Deploye kode til produksjon uten at brukerne ser den nye funksjonaliteten
  • Teste grundig i produksjonsmiljøet (ekte data, ekte last, ekte integrasjoner) uten risiko
  • Aktivere gradvis for testbrukere, interne brukere, eller et utvalg før full lansering
  • Skru av raskt hvis noe går galt, uten ny deployment

Dette lar oss følge trunk-based development og deploye ofte, mens vi samtidig har kontroll over når funksjoner faktisk lanseres til brukerne.

Hvem har ansvar for testing?

Kollektivt ansvar

Kvalitet er alles ansvar, ikke en egen rolle eller fase. Vi følger prinsippet "you build it, you test it, you run it".

Dette betyr ikke at alle gjør alt, men at alle bidrar til kvalitet på ulike måter.

Utviklerens ansvar

Når du skriver kode:

  • Skriv automatiserte tester (unit + integrasjon)
  • Test lokalt før du lager MR
  • Vurder edge cases og feilhåndtering
  • Dokumenter testdata-behov
  • Responder på feil som oppstår i produksjon

Reviewer's ansvar

Når du gjennomgår kode:

  • Sjekk testdekning - er viktig funksjonalitet testet?
  • Kjør endringen lokalt ved behov
  • Vurder risikonivå - trenger vi ekstra testing?
  • Foreslå forbedringer i testene

Felles ansvar

Sammen:

  • Vedlikeholde E2E-tester for kritiske brukerreiser
  • Utforskende testing av nye features
  • Overvåke produksjon og reagere på varsler
  • Dele kunnskap om testing og kvalitet

Miljøer og infrastruktur

Vi har ulike miljøer for ulike formål. Hvert miljø har sin rolle i kvalitetssikringen.

Oversikt

MiljøFormålLevetidDatagrunnlagAutomatisk oppdatering
LokaltUtvikling og rask feedbackSå lenge du jobberSyntetiskAvhenger av tilnærming
CI (midlertidig)Isolerte integrasjonstesterPer pipelineSyntetiskSlettes etter test
CD (midlertidig) 🚧Isolerte E2E-testerPer deploymentSyntetiskSlettes etter test
Adhoc (midlertidig)Forutsigbar testing, kurs, pentestingTil du sletter denSyntetiskNei
TestUtforskende testingPermanentSyntetiskJa (fra main)
ProduksjonLive tjenester med ekte dataPermanentEkteJa (fra main)

🚧 = Planlagt funksjonalitet

Legg merke til at det kun er Test og Produksjon som automatisk oppdateres når arbeid merges til main.

Det syntetiske datagrunnlaget som brukes i miljøene kommer fra testdatabasene våre. Dette sikrer konsistente testdata på tvers av alle tjenester.

Lokalt utviklingsmiljø

Vi har to hovedmåter å jobbe lokalt på:

Docker-basert via studieadm-cli (studieadm-cli local up):

  • Fleksibel: Kjør full stack eller velg spesifikke tjenester med --includes/--excludes
  • Oppdateres automatisk til siste main ved oppstart
  • Innebygde debugging-verktøy (mitm proxy, OTLP telemetry)
  • Eksempel: studieadm-cli local up --includes admissio,supergraf for kun opptak og supergraf

Native utvikling (f.eks. mvn quarkus:dev):

  • Direkte kontroll over kode og versjon
  • Raskere feedback-loop for utvikling av én tjeneste
  • Avhengigheter (databaser, andre tjenester) kjøres vanligvis i Docker Compose
  • Vanlig for backend-utvikling når du kun jobber med én tjeneste

Begge tilnærminger gir tilgang til testdatabaser med testdata.

CI-miljøer (midlertidige)

Disse miljøene ser du aldri direkte - de opprettes automatisk når pipelinen din kjører. Hver pipeline får sitt eget isolerte miljø med egne testdatabaser og mock-services. Dette gjør at mange utviklere kan kjøre integrasjonstester samtidig uten å påvirke hverandre. Når pipelinen er ferdig, slettes alt.

Du trenger ikke gjøre noe spesielt - CI setter opp og rydder automatisk.

CD-miljøer (midlertidige)

Status: Planlagt funksjonalitet

CD-miljøer og tilhørende E2E-tester eksisterer ikke per i dag, men er en del av målbildet vårt. Vi jobber med å etablere denne infrastrukturen som en kritisk del av vår continuous deployment-strategi.

Målbilde: Når kode skal deployes kjøres E2E-tester i et eget isolert miljø. Dette miljøet opprettes automatisk for hver deployment og inneholder alle tjenestene våre, slik at E2E-testene kan validere fullstendige brukerreiser på tvers av systemet.

Hvis E2E-testene passerer, deployes koden til test og produksjon. Hvis de feiler, stoppes deploymenten og miljøet slettes uten at noe når produksjon.

Som med CI-miljøer vil dette være fullt automatisk - CD-pipelinen håndterer alt.

Testmiljø

Testmiljøet speiler produksjon i arkitektur, men med syntetiske data. Bruk det til utforskende testing av nye features, ofte bak feature flags. Perfekt for å demo pågående arbeid eller verifisere at alt fungerer sammen.

Ingen sikkerhet

Testmiljøet har INGEN tilgangskontroll - alle data er tilgjengelig for alle. Test aldri sikkerhetsfunksjoner her, og bruk aldri ekte persondata.

Miljøet oppdateres automatisk ved merge til main, så det kan endre seg flere ganger daglig. Trenger du et stabilt miljø over lengre tid? Bruk adhoc i stedet.

Produksjonsmiljø

Her kjører de faktiske tjenestene våre med ekte data og ekte brukere. Feature flags lar oss deploye ny kode hit uten at brukerne ser den - perfekt for å teste i ekte miljø før lansering.

Status i dag

Noen applikasjoner (Admissio, Min kompetanse) oppdateres automatisk når kode merges til main. Andre applikasjoner krever fortsatt manuelle godkjenninger før produksjonsdeploy. Ingen applikasjoner har E2E-tester som kjører før deployment.

Målbilde: Produksjon oppdateres automatisk etter at E2E-tester har passert i isolerte CD-miljøer. Hvis noe går galt, varsler Zabbix, og vi kan raskt rulle tilbake eller skru av feature flags.

Adhoc-miljøer (midlertidige)

Trenger du et miljø som står stille mens du jobber? Adhoc-miljøer er stabile miljøer som ikke oppdateres automatisk.

Typiske bruksområder:

  • Kurs og opplæring (miljøet endres ikke underveis)
  • Pentesting og sikkerhetstesting (trenger stabilt mål)
  • Langvarig utforskende testing (ingen overraskelser fra andres endringer)
  • Demonstrasjoner med forutsigbart innhold

I dag: Se adhoc-env dokumentasjon.

Målbilde: Adhoc-miljøer opprettes ved å lage adhoc-*-branches i fs-k8s.

Husk: Skru av miljøet når du er ferdig - adhoc-miljøer koster ressurser.

Pipeline-flyten

I målbildet vårt går kodeendringer automatisk fra utvikling til produksjon gjennom en sentralisert pipeline. Diagrammet under viser samme flyt som over, men fra infrastrukturperspektivet: fra merge til main, gjennom automatiske tester, til deployment i både test og produksjon. Nøkkelen er at E2E-tester kjører før deployment, og at alt skjer automatisk uten manuelle godkjenningssteg.

Status per applikasjon

I praksis har hver applikasjon sin egen pipeline, og flere krever fortsatt manuelle steg før produksjonsdeploy. Tabellen under viser hvor langt hver applikasjon har kommet (per desember 2025):

ApplikasjonIntegrasjonstester i CIAuto-deploy testAuto-deploy prodVersjonering
Admissio✅ Automatisk
FS Admin❌ Manuell❌ Manuell🚧 Manuell (Changeset)
Min kompetanse✅ Automatisk
Sis❌ Manuell❌ Manuell✅ Automatisk
Tilgangsstyring❌ Manuell✅ Automatisk
Utdanningsregister❌ Manuell✅ Automatisk

✅ = Implementert | 🚧 = Delvis/pågående | ❌ = Ikke implementert

Viktige observasjoner:

  • Playwright-testene som tester kritiske brukerreiser på tvers av tjenester kjører ikke i pipeline da de tidligere skapte støy i delt testmiljø
  • Admissio og Min kompetanse har full automatisk deployment til produksjon uten manuelle steg
  • Backend-applikasjoner (Admissio, Sis, Tilgangsstyring, Utdanningsregister) har integrasjonstester i CI
  • De fleste applikasjonene krever manuelle steg for produksjonsdeploy
  • På grunn av at alle applikasjonene har egne pipelines for CD, så er det vanskelig å samarbeide om gode E2E-tester på tvers av tjenester før deploy

Testdata

For å teste produktet vårt trenger vi realistiske og sammenhengende testdata. Dette er ekstra utfordrende i vårt system fordi data flyter mellom tjenestene våre. Lærestedene vedlikeholder emner og studieprogrammer i Sis. Disse publiseres jevnlig i Utdanningsregisteret og blir dermed synlige for offentligheten. De publiserte utdanningene tilbys deretter i opptak, slik at søkere kan bli studenter i Sis.

Vi trenger derfor en strategi for hvordan vi utvikler og vedlikeholder testdataene.

Testdatabaser

Hver tjeneste har sin egen sentralt vedlikeholdte database med testdata. Disse databasene sikrer at vi har konsistent og sammenhengende testdata tilgjengelig for utvikling og testing.

Status per tjeneste (per desember 2025):

TjenesteDatabaseTilnærmingDokumentasjonAnsvarlig
SisOracleTemplate-database🚧 ManglerTeam Føniks
Opptak (Admissio)PostgresTemplate-databaseDokumentert🚧 Ubestemt
UtdanningsregisterPostgresMigreringer i kildekode🚧 Mangler🚧 Ubestemt
TilgangsstyringOracleMigreringer i kildekode🚧 Mangler🚧 Ubestemt

To ulike tilnærminger:

Template-database (Sis, Opptak):

  • Sentralt vedlikeholdt database med eksempeldata
  • Hver utvikler/CI-jobb bruker sin egen kopi av template-databasen
  • Testdata vedlikeholdes utenfor kildekoden

Migreringer i kildekode (Utdanningsregister, Tilgangsstyring):

  • Testdata defineres som migreringer i tjenestens kildekode
  • Kjøres automatisk når databasen settes opp
  • Testdata versjoneres sammen med koden

For automatiserte tester

Anbefaling: Lag testdata som del av testen selv. Dette gjør at testen eier dataene sine og står dermed fritt til å slette de før eller etter fullført test. Automatiske tester står også fritt til å etterlate data fra tidligere kjøringer dersom dette oppleves som hensiktsmessig.

Siden automatiserte tester står fritt til å etterlate gamle testdata så er det viktig at automatiserte tester ikke kjøres mot Test eller Produksjon. Tester skal kun kjøre mot midlertidige miljø.

@Test
void testStudentRegistration() {
// Opprett testdata
var student = createTestStudent("12345678");
var course = createTestCourse("INF101");

// Utfør test
var result = registrationService.register(student, course);

// Verifiser
assertTrue(result.isSuccess());
}

Hvorfor:

  • Deterministisk - testen er ikke avhengig av eksisterende data
  • Isolert - tester påvirker ikke hverandre
  • Dokumenterende - testdata viser hva som trengs

Når bruker vi testdatabasene?

Bruk til: Eksempeldata for manuell og utforskende testing.

Vedlikehold: Se tabellen over for ansvarlig per tjeneste. For tjenester med ubestemt ansvar jobber vi med å etablere tydelige roller.

Advarsel: Automatiserte tester kan også bruke strukturer fra testdatabasene, men da må man være villig til å oppdatere disse testene når innholdet i testdatabasene endres.

Best practices

  • ✅ Automatiserte tester: Skap din egen data som del av testen
  • ✅ Manuell testing: Bruk testdatabasen (template-database eller migreringer)
  • ✅ Dokumenter testdata-behov når du oppdager mangler
  • ❌ Ikke bruk produksjonsdata i test
  • ❌ Ikke avheng av spesifikke verdier i testdatabasen i automatiserte tester

Guides

Vil du lære mer om hvordan vi jobber med krav og testing i praksis? Se våre guides:

Veien videre

Dette er en reise. Vi lærer underveis, og vi har kommet langt. Vi leverer allerede kontinuerlig til produksjon for flere av applikasjonene våre. Samtidig erfarer vi at testdekningen vår ikke er høy nok. Dette forhindrer oss fra å levere kontinuerlig over hele linja, og bremser dermed fart og flyt. Derfor må vi fortsette å investere i testing og andre kvalitetsfremmende tiltak.

Vi jobber aktivt med

  1. Integrere E2E-tester i deployment-pipelinen

    • Sette opp isolerte CD-miljøer hvor Playwright-testene kan kjøre uten å skape støy
    • Sjekke ut og kjøre testene fra sta-funksjonell-testing-playwright som del av CD-pipeline
    • Forbedre integrasjonstester i eksisterende tjenester
  2. Feature flag-praksis

    • Dokumentere hvordan vi bruker feature flags
    • Etablere rutiner for opprydding av gamle flags
  3. Forbedre lokalt utviklingsmiljø

    • Gjøre studieadm-cli enda enklere å bruke
    • Raskere oppstart og mindre ressursbruk
  4. Observerbarhet

    • Bedre dokumentasjon av LGTM-stack
    • Dashboards for vanlige problemstillinger

Videre lesning


Tilbakemelding

Dette dokumentet er et levende dokument som utvikles kontinuerlig. Har du forslag til forbedringer? Lag en MR eller ta det opp med teamet.