onbetrouwbare TESTS
TESTS DIE SOMS FALEN
- "Oh ja, die test faalt wel eens"
- Sneeuwbaleffect in de testsuite
- Veel voorkomend probleem
- "non deterministic tests", "flaky tests", "random tests", "erratic tests", "brittle tests", "flickering tests" of zelfs "heisentests"
onbetrouwbare tests
- Kan in de kosten lopen
- Veel werk om te verhelpen
- Houden pipelines en deployments tegen
- Onbetrouwbare test kan wijzen op onderliggende fouten
Patronen
die leiden tot onbetrouwbare tests
Hard coded ids
- Postgres gebruikt sequences voor ids
- Auto increment (1, 2, 3)
- Test frameworks doen vaak een rollback
- Rollbacks resetten niet de sequences
-
assert_equal Ivaldo.last.id, 1
Hard coded ids
ActiveRecord::Base.transaction do
puts Ivaldo.create!.id
# 1
raise ActiveRecord::Rollback
end
puts Ivaldo.create!.id
# 2
database ordering
- Aannames over de volgorde
- Niet-gesorteerde collecties
- Zelfs ordering op id is nodig
Database ordering
[8] pry(main)> User.order('id desc').find_by(name: 'sam').id
User Load (7.6ms) SELECT "users".* FROM "users" WHERE "users"."name" = 'sam' ORDER BY id desc LIMIT 1
=> 25527
[9] pry(main)> User.order('id').find_by(name: 'sam').id
User Load (1.0ms) SELECT "users".* FROM "users" WHERE "users"."name" = 'sam' ORDER BY id LIMIT 1
=> 2498
[10] pry(main)> User.find_by(name: 'sam').id
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."name" = 'sam' LIMIT 1
=> 9931
Aannames over tijd
- 16:55 uur onze tijd
- Japan: 23:55 uur
- Records van 'vandaag' kunnen andere uitkomst hebben
- Timecop!
Aannames over tijd
sleep 0.001
assert(elapsed < 1)
Aannames over tijd
sleep 0.001
assert(elapsed < 1)
- Bij een hoge load kunnen CPU hold ups ontstaan
- Onvoldoende time outs in integratie tests
- Klik op een link, check of er een element is
- Tijd wanneer een element gerenderd is
Global state
- Test past globale variabelen aan die niet gereset worden
- Falende tests in bepaalde volgorde
- Dit kan ook voorkomen bij caching
- Tests uitvoeren in willekeurige volgorde
Global state
class Frog
cattr_accessor :total_jumps
attr_accessor :jumps
def jump
Frog.total_jumps = (Frog.total_jumps || 0) + 1
self.jumps = (self.jumps || 0) + 1
end
end
def test_global_tracking
assert(Frog.total_jumps.nil?)
end
def test_jumpy
frog = Frog.new
frog.jump
assert(frog.jumps == 1)
end
Aannames over omgeving
- Logica wat anders werkt in verschillende omgevingen
- Bijvoorbeeld afbeeldingen downloaden indien genoeg ruimte
- Test faalt indien schijfruimte niet opgehaald mag worden
Resource leaks
- Integratietests op grote schaal
- Event handlers niet opruimen
- Snelle tests kunnen traag worden of helemaal stuk gaan door lekken
- v8 heat dumps na testen om geheugengebruik van Chrome in de gaten te houden
- Verwarrend op testomgevingen met mindere 'prestaties'
Problemen aanpakken
- Verwijderen en vergeten
- Minder test coverage, scheelt tijd
- 100 tests / 100%, 200 tests / 60%
- Opnieuw uitvoeren tot groene pipeline
- Kapotte code en langzame test suite
- CI kan vaker falen dan lokaal (waar ligt de grens?)
- Niets doen
- Komt vaker voor dan je denkt
- Moeilijk om tests te verwijderen
Problemen aanpakken
- Quarantaine en oplossen
- Test overslaan en eraan herinnerd worden
- Tijdelijk minder coverage tot er tijd is om te fixen
CONCLUSIE
- Zweven tussen de 4 oplossingen
- Overleggen wat te doen met onbetrouwbare tests
- Gewoonte kan erin roesten
- Bitbucket deploys
- Tests uitvoeren in random volgorde met seed nummer
- Is de test onbetrouwbaar of lag het aan de environment?
- Tests uitvoeren in continue loop in losse omgeving
- Last resort: debug code om extra informatie te loggen
Onbetrouwbare tests
By Dimitri Snijder
Onbetrouwbare tests
- 45