Viser arkivet for stikkord testing

Speed up tests!

Har akkurat publisert en gem for raskere testing.
Gonzales er en gem for å speede opp tester når du bruker FactoryGirl. Ved bruk av Gonzales har jeg selv klart å redusere tiden på unit-testene fra 1 minutt og 42 sekunder til 28 sekunder på ett prosjekt som jeg jobber med. Det er en reduksjon på omtrent 70%.
Du trenger i hovedtrekk å lære deg bare ett uttrykk: speedy. speedy benyttes for å:

  • lagre factories til test-databasen før testene skal kjøres.
  • deklarere assosiasjoner i factories
  • referere til objekter som er lagret i test-databasen før testene startet

Kildekoden til Gonzales finnes her. Dokumentasjon finnes her.

Happy speedy coding.

Hvordan tester du Rails-applikasjonen din?

Vi driver og setter opp nye unit-tester fra scratch for Origo-applikasjonen. Det er lenge siden jeg skrev Rails-tester, så jeg skulle gjerne hatt en liten dialog om hvordan dette best gjøres.

Personlig har jeg aldri vært komfortabel med måten Rails sitt testrammeverk på jobber sammen med databasen på. Først og fremst mener jeg fixtures er et helt urealistisk, overoptimistisk system. I mine øyne burde tester alltid fungere sammen med en eksisterende database. Jeg lurer på om denne tesen faktisk holder vann.

Når jeg kjører rake test:unit er Rails så ambisiøs at den faktisk lager en tom database og setter opp schemaet. Det ser ut til at den faktisk kjører en dump og restore av schemaet i development-databasen (i vårt tilfelle med pg_dump). Resultatet er en tom database, og tanken er at man skal fylle denne med testdata via fixtures.

I Origos tilfelle er det mye bootstrap-informasjon som lagres i databasen. Sikkerhetssystemet er for eksempel implementert ved at alle “superbruker”-operasjoner utføres av en spesielt privilegert bruker — systembrukeren, som faktisk eksisterer som en vanlig brukerkonto i systemet. Dette er for å ha et konsekvent system som unngår null-verdi-problemet. I tillegg til denne kontoen er roller, rollegrupper og rettigheter også representert som databaseobjekter.

(Om noen lurer: Å representere slik metadata i databasen er supert, men har i ettertid bitt oss i leggen rent ytelsesmessig. Siden nesten alle objekter i databasen refererer til roller, sikkerhetsregler osv. betyr det at vi foretar millioner av SQL-queries for å hente data som sjelden endrer seg, for å laste alle disse ekstra objektene. Caching er ingen fullgod løsning, og vi ser oss nødt til å hardkode det meste inn i koden snart.)

Så i vårt tilfelle er det en basiskonfigurasjon som må være på plass for at noe som helst skal fungere, og det virker upraktisk å fylle den tomme databasen med denne hver gang testene kjøres. Ikke umulig, bare upraktisk.

I tillegg virker det som om mange skriver tester som antar et eksklusivt testunivers som eksisterer i et vakuum. Et (forenklet) eksempel er noe slikt, som antar at det ikke finnes noe data fra før:

Post.create!(...)
assert_equal 1, Post.all.count

I praksis vil man kanskje ikke telle, men gjøre noe sånt som:

p = Post.create!(...)
assert_equal [p], Post.find_all_using_my_fancy_finder(...)

Rails implementerer dessuten transaksjonelle tester slik at “søppel” fra hver test kan ryddes opp, og vakuumet holdes like lufttomt hele tiden.

I stedet for å starte på bar bakke hver gang foreslår jeg et testoppsett som kjører direkte på development-databasen. Dette er baserrt på observasjonen at tester alltid burde fungere uansett hva som allerede er i databasen. I den grad tester faktisk tester hvorvidt koden fungerer korrekt, er dette en mer robust måte å kjøre tester på. Du vil på denne måten kunne teste feil som oppstår når databaseobjekter interagerer (med objekter som allerede er i databasen) på måter du ikke har forutsett.

Og ikke minst: Det er raskere å kjøre tester om man slipper å lage en ny database hver gang.

Jeg har prøvd å finne ut hvordan folk i praksis tester databasemodellene sine, men alt jeg finner er ganske “vanilla”, med relativt enklere antagelser og ikke spesielt ambisiøse krav. Hvordan gjør dere det? Råd og vink mottas med takk.