Week 1 (46) Basics of Testing Summary

This is an AI generated summary from the transcription.

Föreläsning 1 – Basics of Testing

Presentationsbilder – Basics of Testing 2026

Vad är mjukvarutestning?

Mjukvarutestning handlar om att undersöka om ett program gör det som förväntas och att upptäcka fel, helst innan programmet når användarna.

Grunden är att jämföra två saker:

  • Expected behaviour: Vad vi förväntar oss att systemet ska göra.
  • Actual behaviour: Vad systemet faktiskt gör när vi kör det.

När dessa skiljer sig åt kan vi ha upptäckt ett fel.

Testning hjälper oss att:

  • Upptäcka buggar och andra problem.
  • Skapa förtroende för att programvaran fungerar.
  • Förstå hur ett befintligt system faktiskt beter sig.
  • Få en bättre förståelse för kraven och användarnas behov.

Ett exempel från föreläsningen var spelet Timebreaker, där en svårupptäckt bugg i en grafikdrivrutin kunde orsaka en Blue Screen of Death. Felet uppstod bara under vissa omständigheter och tog sex månader att isolera.

Exemplet visar att buggar kan vara svåra att reproducera och att problemen inte nödvändigtvis ligger i vår egen kod.

Validering och verifiering

Två grundläggande begrepp inom mjukvarutestning är validation och verification.

Validation – bygger vi rätt produkt?

Validering handlar om att undersöka om systemet uppfyller användarnas verkliga behov.

  • Löser systemet användarens problem?
  • Är produkten användbar och tillräckligt bra?
  • Behöver kraven eller specifikationen förändras?
  • Är det rätt funktionalitet vi utvecklar?

Ett system kan uppfylla samtliga specificerade krav men ändå vara svårt att använda eller misslyckas med att lösa användarens problem.

Verification – bygger vi produkten rätt?

Verifiering handlar om att undersöka om implementationen överensstämmer med specifikationen.

Vi jämför systemets faktiska beteende med dess funktionella och icke-funktionella krav.

En implementation kan innehålla:

  • Correctly implemented functionality: Funktioner som beter sig enligt specifikationen.
  • Faults: Funktioner som är implementerade men beter sig felaktigt.
  • Omissions: Funktionalitet som finns i specifikationen men ännu inte är implementerad.
  • Surprises: Funktionalitet i implementationen som inte beskrivs i specifikationen.

Testning ger oss information om systemets beteende, men kan normalt inte bevisa att programmet är korrekt för alla tänkbara indata. Det är viktigt att skilja vanlig testning från formal program verification, där man använder formella metoder för att bevisa egenskaper hos ett program.

Testing vs Checking

Föreläsningen skiljer på två aktiviteter.

Testing är den kreativa processen där vi utformar tester. Vi behöver avgöra:

  • Vad vi vill undersöka och varför.
  • Vilken del av systemet som ska testas (System Under Test, SUT).
  • Vilka specifika indata vi ska använda.
  • Vilket resultat vi förväntar oss.

Checking är att köra ett redan definierat test och kontrollera resultatet.

Att skriva ett enhetstest är alltså testing, medan exempelvis npm run test utför automatiserad checking.

Automatisering är särskilt värdefull eftersom samma kontroller ofta behöver upprepas många gånger.

Testning som innebär att vi kör programmet är en dynamisk aktivitet. Statiska kvalitetskontroller, som kodgranskning och lintning, undersöker i stället kod utan att köra programmet.

Från bug till observerbart fel

Ett viktigt avsnitt beskriver hur en bugg i koden kan leda till ett synligt fel.

Centrala begrepp

  • Fault / Defect / Bug: Ett statiskt fel i källkoden, konfigurationen eller liknande.
  • Program state: Programmets tillstånd, exempelvis värden i variabler, databaser och annan lagring.
  • Error state: Ett felaktigt programtillstånd som uppstått till följd av en bugg.
  • Failure: Ett observerbart felaktigt beteende.

Ett exempel är en add(a, b)-funktion som av misstag subtraherar i stället för att addera.

Om vi anropar add(3, 4) får vi -1 i stället för 7. Buggen har orsakat ett felaktigt tillstånd.

Men om vi anropar add(3, 0) blir resultatet 3 oavsett om funktionen adderar eller subtraherar. Buggen finns fortfarande, men just det testet avslöjar den inte.

Tre villkor för att upptäcka ett fel

  1. Reachability: Testet måste nå och exekvera koden där buggen finns.
  2. Infection: Buggen måste orsaka ett felaktigt programtillstånd.
  3. Propagation: Det felaktiga tillståndet måste sprida sig så att det ger ett observerbart fel.

Det räcker alltså inte att köra kod som innehåller en bugg. Testet måste också få buggen att påverka beteendet på ett sätt som vi kan upptäcka.

Vad gör kod testbar?

En viktig egenskap hos mjukvara är testability, alltså hur lätt det är att testa koden.

Två centrala egenskaper är:

Controllability – kontrollerbarhet

Möjligheten att styra systemet till ett specifikt tillstånd och exekvera den kod vi vill undersöka. Exempelvis att kunna anropa en funktion med valfria argument eller kontrollera vilka data som finns i databasen.

Observability – observerbarhet

Möjligheten att se vad som händer i systemet och upptäcka om beteendet är felaktigt.

Tillsammans bidrar dessa till hög testbarhet.

Ytterligare mål med testfall är:

  • Accuracy: Hur väl ett test hjälper oss att isolera var ett fel finns.
  • Traceability: Hur väl vi kan koppla tester till den kod som körs och de krav som ska uppfyllas.

Enhetstester är ofta användbara eftersom de låter oss undersöka små delar av systemet isolerat, vilket förenklar felsökningen.

Testfall och testorakel

Ett testfall (test case) behöver innehålla eller definiera följande:

  • System Under Test (SUT): Vad som ska testas, exempelvis en funktion, klass eller komponent.
  • Specific input: De exakta indata som används.
  • Expected output: Det förväntade resultatet.
  • Actual output: Det resultat som systemet faktiskt producerar.
  • Observed output: Det resultat som testaren eller testverktyget observerar.

Därefter behöver vi avgöra om det observerade beteendet stämmer med det förväntade.

Den som gör denna bedömning kallas test oracle, eller testorakel.

Ett orakel kan exempelvis vara en assert eller expect i ett enhetstest, men kan också vara en människa som bedömer resultatet.

Oracle problem

Hur vet vi vilket resultat som faktiskt är korrekt?

Detta är ett grundläggande problem inom testning och blir särskilt viktigt vid AI-genererad testning.

En AI-agent kan skapa tusentals tester, men om den har missförstått specifikationen kan alla tester kontrollera fel saker.

Att ett test passerar betyder alltså inte automatiskt att systemet gör rätt sak. Vi måste också kunna lita på testets förväntningar och bedömningar.

Reproducerbarhet och Flaky Tests

Vi vill att tester ska vara deterministiska: samma förutsättningar och samma indata ska ge samma resultat.

Ett flaky test är ett test som ibland lyckas och ibland misslyckas utan att vi avsiktligt förändrat det som testas.

Vanliga orsaker är:

  • Tidsberoenden, exempelvis datum och klockslag.
  • Timing och parallell exekvering.
  • Skillnader i hårdvara eller programmiljö.
  • Att tester påverkar varandras tillstånd.
  • Otydliga testinstruktioner eller olika tolkningar.
  • Felaktiga tester.

Föreläsningen innehöll ett exempel där studenter testade samma webbapplikation med samma instruktioner men kom fram till olika resultat. Ett datumfel kunde dessutom bara upptäckas under vissa veckodagar.

Det visar vikten av tydliga testfall och kontrollerade testförutsättningar.

För att göra tester mer reproducerbara kan vi exempelvis återställa databaser mellan tester, kontrollera systemklockan och använda bestämda startvärden för slumptalsgeneratorer.

Input Domain, Correctness och Reliability

Input domain (input space) är mängden av alla möjliga indata till ett program.

Correctness innebär att programmet beter sig korrekt för varje möjligt indata i denna mängd.

Reliability beskrivs i föreläsningen som sannolikheten att programmet fungerar korrekt när vi väljer ett slumpmässigt indata ur inputdomänen.

Test Selection Problem

Vi kan normalt inte testa alla möjliga indata. Även en enkel funktion med två heltalsargument kan ha ett enormt antal kombinationer.

Att testa samtliga kombinationer kallas exhaustive testing och är oftast praktiskt omöjligt.

Vi måste därför välja ett begränsat antal testfall som har god möjlighet att avslöja fel. Detta är test selection problem.

Det handlar inte bara om att skriva många tester utan om att välja rätt tester.

Hur mycket testning som behövs beror också på systemets användningsområde och konsekvenserna av ett fel. Ett fel i ett datorspel kan ha helt andra konsekvenser än ett fel i ett säkerhetskritiskt system.

Testnivåer

Vi skiljer mellan olika nivåer av testning beroende på hur stor del av systemet vi undersöker.

  • Unit Testing: Testning av en liten isolerad del, exempelvis en metod, klass eller komponent.
  • Integration Testing: Testning av hur flera komponenter fungerar tillsammans, exempelvis frontend och backend.
  • System Testing: Testning av hela det integrerade systemet, inklusive dess olika komponenter.

Andra anledningar att testa

  • Beta Testing: En begränsad grupp användare får prova systemet före en bredare lansering.
  • Acceptance Testing: Kunden eller beställaren undersöker om systemet kan accepteras och uppfyller behoven.
  • Regression Testing: Tidigare tester körs igen efter förändringar för att undersöka om något som fungerade tidigare har gått sönder.

Regressionstestning är särskilt värdefull i ett utvecklingsarbete där programvaran förändras kontinuerligt.

Olika testtekniker

Black-box Testing

Vi testar systemets funktionalitet utifrån, utan att behöva känna till dess interna implementation.

Tester kan skapas utifrån specifikationer och förväntat beteende. Vi bryr oss om vad systemet gör, inte hur koden är skriven.

White-box Testing

Vi utformar tester med kunskap om systemets interna kod.

Vi kan exempelvis undersöka vilka grenar i if-satser som exekveras och använda kodtäckning (code coverage) för att identifiera kod som inte testats.

Smoke Testing

Vi kör delar av ett system och letar efter tydliga tecken på fel, exempelvis:

  • Programkrascher eller exceptions.
  • Felmeddelanden och loggmeddelanden.
  • Misslyckade assertions.
  • Utebliven output.
  • Ett program som fastnar eller förbrukar orimligt mycket resurser.

Random Testing

Vi genererar slumpmässiga indata och undersöker hur systemet reagerar.

Det kan vara användbart när det är lätt att generera stora mängder indata och identifiera felaktigt beteende.

Ett exempel är testning av kompilatorer, där vi kan generera kod som borde vara syntaktiskt korrekt respektive felaktig och kontrollera hur kompilatorn reagerar.

Testning i en tid med AI-agenter

AI-agenter kan hjälpa oss att skapa och köra tester, men de förändrar inte de grundläggande principerna för mjukvarutestning.

Tvärtom blir flera frågor ännu viktigare:

  • Hur vet vi att agenten har förstått kraven?
  • Hur avgör vi om de genererade testerna är korrekta?
  • Hur kan vi spåra tester till specifikationer och implementation?
  • Hur säkerställer vi att testerna är reproducerbara?
  • Hur upptäcker vi funktionalitet som agenten lagt till utan att vi bett om det?

Automatiserade regressionstester kan också hjälpa oss att bevara överenskommet beteende när vi fortsätter utveckla ett system.

En viktig tanke från föreläsningen är att människans ansvar för validering blir särskilt betydelsefullt när AI-agenter tar över mer av implementationen och verifieringen.

Vi behöver fortfarande kunna förstå, utvärdera och ifrågasätta både testerna och det system som utvecklas.

Sammanfattning

Det viktigaste från föreläsningen är att:

  1. Testning jämför förväntat och faktiskt beteende för att upptäcka problem.
  2. Verifiering undersöker om vi bygger produkten rätt, medan validering undersöker om vi bygger rätt produkt.
  3. En bugg leder inte nödvändigtvis till ett observerbart fel.
  4. Testbarhet bygger bland annat på kontrollerbarhet och observerbarhet.
  5. Bra testfall kräver tydliga indata, förväntade resultat och ett tillförlitligt testorakel.
  6. Tester måste vara reproducerbara för att ge användbar information.
  7. Vi kan inte testa allt och behöver därför göra genomtänkta val av testfall.
  8. Olika testnivåer och testtekniker upptäcker olika typer av problem.
  9. AI-genererade tester behöver granskas och värderas precis som andra tester.

Det centrala är inte hur många tester vi har, utan vilket förtroende de ger oss för att systemet fungerar som det ska.

Fortsatt arbete

Fortsätt med materialet i veckoguiden för vecka 1.

Påbörja också arbetet med Assignment 1. För den som väljer TDD-alternativet rekommenderas särskilt det kompletterande materialet om testdriven utveckling.