Testing

Testing#

Du har sikkert brukt print-funksjonen for å teste om koden fungerer som den skal. Nå skal vi skrive tester for å forutse hvordan vi forventer at koden skal fungere.

Enkel testing#

For å teste enkle ting kan vi bruke assert.

La oss ta utgangspunkt i en betalingsmodell. Funksjonen pris skal ta et heltall alder og et flyttall vanlig_pris som argumenter og returnere et flyttall pris som representerer en pris.

  • Eldre (\(67\) år eller over) skal betale \(50\%\) av vanlig pris.

  • Barn fra og med \(5\) år og yngre har gratis billett.

  • Barn fra og med \(6\) år til og med \(17\) år skal betale \(40\%\) av vanlig pris.

Før vi begynner å skrive funksjonen kan vi allerede vite et par ting. Hvis vanlig pris er \(100\) kr så vil disse tingene stemme:

  • Billetten for en som er \(67\) år vil koste \(50.0\) kr.

  • Billetten for en som er \(6\) år vil koste \(40.0\) kr.

  • Billetten for en som er \(5\) år vil koste \(0.0\) kr.

  • Billetten for en som er \(18\) år vil koste \(100.0\) kr.

Vi kan sjekke disse testtilfellene (testcases) i Python med assert.

assert pris(67, 100) == 50.0
assert pris(6, 100) == 40.0
assert pris(5, 100) == 0.0
assert pris(18, 100) == 100.0

Her kommer et eksempel på hele programmet:

def pris(alder : int, vanlig_pris : int) -> float:
    if alder >= 67:
        return vanlig_pris * 0.5
    elif alder < 6:
        return 0.0
    elif alder < 18:
        return vanlig_pris * 0.4
    else:
        return float(vanlig_pris)

assert pris(67, 100) == 50.0, "Prisen stemmer ikke for en 67-åring"
assert pris(6, 100) == 40.0, "Prisen stemmer ikke for en 6-åring"
assert pris(5, 100) == 0.0, "Prisen stemmer ikke for en 5-åring"
assert pris(18, 100) == 100.0, "Prisen stemmer ikke for en 18-åring"

Hvis noen av testene feiler får vi en feilmelding med AssertionError og den stringen vi har skrevet etter komma.

Endrer vi prisen for alder < 6 til noe annet enn 0.0 får vi nå en feilmelding:

AssertionError: Prisen stemmer ikke for en 5-åring

Merk

Det er ikke så vanlig å ha testene i samme fil som den koden som skal testes. Vanligvis skriver man tester i en egen .py-fil og importerer det man skal teste fra en annen .py-fil.

For eksempel kan man teste billettsystem.py ved å lage en fil test_billettsystem.py og importere funksjonen man skal teste med import pris from billettsystem.

En advarsel

Det kan være fristende å bruke assert i en funksjon for å sjekke om argumentene til funksjonen er riktig, men dette anbefales ikke fordi asserts ignoreres når Python kjøres med -O i terminalvinduet.

I stedet for:

def tid(s, v):
    assert v != 0
    assert isinstance(s, (int, float)) == True
    assert isinstance(v, (int, float)) == True
    return s/v

Bruk heller:

def tid(s, v):
    if isinstance(s, (int, float)) == True and isinstance(v, (int, float)) == True:
        if v != 0:
            return s/v
        else:
            raise ZeroDivisionError("Du kan ikke dele på null.")
    else:
        raise TypeError("Bare tall er akseptert som argument.")

Oppgaver#

Oppgave 1 🎫

Her kommer et eksempel på et annet billettsystem.

  • Barn \(9\) år og under kommer inn gratis.

  • Ungdom (til og med \(17\) år) får et avslag på \(30\%\).

  • Eldre (\(60\) år og over) får et avslag på \(20\%\).

Under kommer et eksempel hvor pris-funksjonen har flere feil.

def pris(alder : int, vanlig_pris : int) -> float:
    if alder < 9:
        return 0.0
    elif alder <= 17:
        return vanlig_pris * 0.3
    elif alder > 60:
        return vanlig_pris * 0.2
    else:
        return float(vanlig_pris)
    
assert pris(34, 50) == 50.0
assert pris(9, 50) == 0.0
assert pris(17, 50) == 35.0
assert pris(60, 50) == 40.0
  1. Vil noen av testtilfellene med assert gå gjennom uten feilmelding?

  2. Fiks funksjonen slik at koden går gjennom alle testtilfellene uten feilmelding.

Oppgave 2 🧬

I denne oppgaven skal vi feilsøke kjede med DNA.

DNA

DNA-molekyler inneholder informasjon som basepar. DNA har en struktur som en dobbel heliks. Man kan tenke på det som to lange tråder med trappetrinn mellom, litt som en spiraltrapp.

  • Hvis det er en "A" på den ene siden, så er det en "T" på den andre siden.

  • Hvis det er en "T" på den ene siden, så er det en "A" på den andre siden.

  • Hvis det er en "C" på den ene siden, så er det en "G" på den andre siden.

  • Hvis det er en "G" på den ene siden, så er det en "C" på den andre siden.

Her kommer to sider av et DNA-molekyl, men det skjuler seg en feil i den genetiske koden… en mutasjon!

oppe = "TTCCAAATGTGAAGTCCCACTATATATAAGTACCTTGATGGACCCAGAGATATAAACGTGCGTCGTACCTAGAAATTAAA"
nede = "AAGGTTTACACTTCAGGGTGATATATATTCATGGAACTACCTGGGTCTCTCTATTTGCACGCAGCATGGATCTTTAATTT"

Bruk assert i en for-løkke for å finne hvor feilen ligger på DNA-tråden. Rett så opp i feilen.

Oppgave 3 ✖️

Fakultet (factorial) er definert på denne måten:

  • \(0! = 1\)

  • \(n! = n \cdot (n-1) \cdot (n-2) \cdot ... \cdot 3 \cdot 2 \cdot 1\)

Du skal lage en funksjon fakultet(n) som tar et heltall n som argument og returnerer \(n!\), men først…

  1. Lag tre testtilfeller med assert for hvordan du forventer at funksjonen fakultet(n) skal fungere.

  2. Lag funksjonen fakultet(n) og sørg for at testene går gjennom uten feilmelding.

Oppgave 4 🧲

Andregradslikninger på formen \(ax^2 + bx + c = 0\) har løsningene \(x_1=\frac{-b+\sqrt{b^2-4ac}}{2a}\) og \(x_2=\frac{-b-\sqrt{b^2-4ac}}{2a}\).

Du skal lage en funksjon som finner løsningene for en andregradslikning, men først…

  1. Bruk din matematiske intuisjon, en graftegner eller internett for å finne tre forskjellige andregradslikninger hvor man har ingen løsning, én løsning eller to løsninger.

  2. Gjør likningene du fant om til testtilfeller med assert.

  3. Lag funksjonen slik at koden går gjennom testtilfellene uten feilmelding.