Slechte tests: Wat de oorzaak ervan is en hoe u ze voorgoed kunt oplossen
Een test die willekeurig mislukt, leert uw team mislukkingen te negeren. Leer hoe u kunt bevestigen dat een test slecht is, spoor de echte oorzaak op en repareer deze, met demo’s die laten zien wat echt werkt.
- Een slechte test slaagt en faalt op dezelfde code, zonder enige verandering.
- De gebruikelijke oorzaken zijn timing, gedeelde status, onstabiele gegevens, externe services en omgevingsverschillen.
- Vervang vaste slaapperioden door wachttijden voor een specifieke aandoening en geef elke test zijn eigen gegevens.
- Nieuwe pogingen kunnen echte bugs verbergen. Plaats een slechte test in quarantaine, repareer hem binnen een bepaalde termijn of verwijder hem.
De build wordt rood, iemand klikt op “opnieuw uitvoeren”, de build gaat voorbij en iedereen gaat verder. Het lijkt onschuldig, maar slechte tests vernietigen langzaam het meest waardevolle dat een testsuite heeft: het vertrouwen van mensen erin. Wanneer fouten meestal ruis zijn, worden echte bugs er ook doorheen gezwaaid.
In deze gids wordt beschreven waarom tests onnauwkeurig worden en hoe u elke oorzaak op de juiste manier kunt oplossen. We hebben de timing- en gedeelde-statusdemo’s uitgevoerd in Python 3.13, en de getoonde cijfers zijn afkomstig van die runs. Het zijn kleine demonstraties die zijn ontworpen om de schilfering gemakkelijk zichtbaar te maken, zodat uw eigen faalpercentages zullen verschillen.
Wat is een vloktest?
Een vlokkige test geeft verschillende resultaten op dezelfde code. Het gaat de ene run door en mislukt de volgende, ook al is er niets aan het product veranderd. Het kan zijn dat de test een fout bevat, of dat de code een echte periodieke bug bevat. Die tweede mogelijkheid is precies de reden waarom je ze niet zomaar van de hand moet wijzen.
Waarom vlokkige tests zo duur zijn
- Verloren vertrouwen. Zodra mensen aannemen dat rood ‘waarschijnlijk schilferig’ betekent, stoppen ze met het onderzoeken van echte mislukkingen.
- Verspilde tijd. Ontwikkelaars besteden uren aan het opnieuw uitvoeren van pipelines en het onderzoeken van fouten die niets met de wijziging te maken hebben.
- Langzamere levering. Samenvoegingen en releases wachten op herhalingen, waardoor het punt van CI/CD wordt ondermijnd.
- Verborgen bugs. Raceomstandigheden in het product kunnen op testgeluid lijken, en dat zijn de problemen die tijdens de productie pijn doen.
Veelvoorkomende oorzaken in één oogopslag
| Oorzaak | Typisch teken | Gebruikelijke oplossing |
|---|---|---|
| Timing en asynchroon | Mislukt meer wanneer CI bezet is | Wacht op een voorwaarde |
| Gedeelde staat | Mislukt, afhankelijk van de testvolgorde | Isoleer gegevens, reset de status |
| Datums en willekeur | Mislukt op bepaalde tijden of dagen | Controleer de klok en zaad |
| Broze selectors | Mislukt na wijzigingen in de gebruikersinterface | Gebruik stabiele plaatsbepalers |
| Externe diensten | Mislukt wanneer een afhankelijkheid traag is | Bespot of stomp ze |
| Omgevingsverschillen | Lokaal geslaagd, mislukt in CI | Uitlijnen met containers |
| Beperkingen van hulpbronnen | Mislukt bij parallelle uitvoeringen | Stem werknemers af, los conflicten op |
Oorzaak 1: Timing en vaste slaapplaatsen
Dit is de meest voorkomende oorzaak. Bij een test wordt ervan uitgegaan dat het achtergrondwerk na een vaste vertraging wordt afgerond. Om het effect te zien, hebben we een kleine test geschreven waarbij het werk maximaal 2 ms duurt, terwijl de test slechts 1 ms wacht voordat hij controleert:
def flaky_timing():
result = {}
def work():
time.sleep(random.uniform(0, 0.002))
result["done"] = True
threading.Thread(target=work).start()
time.sleep(0.001) # hope it has finished
assert result.get("done")
De oplossing is wachten op de werkelijke toestand, met een royale bovengrens:
def fixed_timing():
result = threading.Event()
def work():
time.sleep(random.uniform(0, 0.002))
result.set()
threading.Thread(target=work).start()
assert result.wait(timeout=1) # continues as soon as it is ready
We hebben elke versie 200 keer uitgevoerd:
| Versie | Mislukkingen van de 200 |
|---|---|
| Vaste slaap | 102 |
| Wacht op een voorwaarde | 0 |
Het resultaat is meer dan alleen betrouwbaarheid. De vaste versie beweegt mee op het moment dat het werk klaar is, dus meestal ook sneller. Een langere slaapstand zou ervoor zorgen dat de eerste versie minder vaak faalt, maar elke run zou dan langzamer zijn, en deze zou nog steeds kunnen mislukken op een drukke CI-machine.
Bij browsertests geldt hetzelfde principe. Gebruik het automatische wachten van Toneelschrijver, of expliciete Selenium wacht, in plaats van time.sleep:
# Playwright: retries until true or a timeout is reached
expect(page.get_by_text("Order placed")).to_be_visible()
# Selenium: an explicit wait for a condition
WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.ID, "confirmation"))
)
Oorzaak 2: gedeelde status en testopdracht
Wanneer tests gegevens delen, kan de ene de andere beïnvloeden, en het resultaat hangt af van de volgorde waarin ze worden uitgevoerd. Deze twee tests gebruiken één gedeelde lijst:
store = []
def test_add():
store.append("x")
assert len(store) == 1
def test_empty():
assert store == []
| Bestel | test_add | test_empty |
|---|---|---|
| leeg en voeg dan toe | Pass | Pass |
| toevoegen en vervolgens leegmaken | Pass | Mislukt |
Onze run bevestigde dit, en daarom kan een suite maandenlang meegaan en dan kapot gaan als iemand parallelle runs of een willekeurige volgorde-plug-in toevoegt. De remedie is isolatie:
- Geef elke test nieuwe gegevens, met behulp van een apparaat dat de test instelt en opruimt. Onze pytest-tutorial laat zien hoe.
- Creëer unieke records, zoals een e-mail met een willekeurig achtervoegsel, in plaats van een gedeelde “testgebruiker”.
- Verpak databasetests in een transactie en draai deze terug, voor zover uw configuratie dit toestaat.
- Voer regelmatig tests in willekeurige volgorde uit, zodat deze problemen vroegtijdig opduiken.
Oorzaak 3: Datums, tijdzones en willekeur
Een toets die de hele week doorgaat, kan mislukken op zaterdag, om middernacht of op de laatste dag van de maand. Maak van tijd een input, in plaats van de klok in uw logica af te lezen:
def is_weekend(today=None):
today = today or datetime.date.today()
return today.weekday() >= 5
is_weekend(datetime.date(2026, 11, 7)) # True (a Saturday)
is_weekend(datetime.date(2026, 11, 9)) # False (a Monday)
Die tests geven op elke dag hetzelfde antwoord. Stel op dezelfde manier een vast zaad in voor willekeurige waarden in tests, en log dit wanneer u willekeurige gegevens gebruikt, zodat u een fout kunt reproduceren. Ga niet uit van een bepaalde volgorde van sets, woordenboeken uit externe bronnen of databasequery’s zonder ORDER BY.
Oorzaak 4: brosse selectors
Een test die een knop met div:nth-child(3) > span vindt, breekt bij elke aanpassing van de lay-out, en mislukt mogelijk alleen voor bepaalde schermformaten of varianten. Geef de voorkeur aan locators op basis van wat gebruikers zien, of aan speciale testattributen. In onze gids voor Playwright-locators wordt de volgorde uitgelegd waarin je ze kunt uitproberen. Animaties zijn een andere vaak voorkomende bron van problemen, dus overweeg ze uit te schakelen in testomgevingen.
Oorzaak 5: Externe diensten en netwerken
Als een test een echte betalingsprovider of een API van derden aanroept, hangt het resultaat af van de uptime en tarieflimieten van iemand anders. Bespot deze oproepen voor de meeste tests en houd een klein aantal afzonderlijke contractcontroles bij ten opzichte van de echte service, zodat u nog steeds leert of deze verandert. In onze gids voor unit- versus integratietests wordt uitgelegd hoe u deze kunt opsplitsen.
Tip: ‘Lokaal geslaagd, mislukt in CI’ betekent meestal een verschil in snelheid, tijdzone, bronnen of versies. Als u tests uitvoert in dezelfde containerimage, lokaal en in CI, worden veel hiervan verwijderd. Onze Docker Compose-tutorial kan hierbij helpen.
Hoe een schilferige test te diagnosticeren
- Bevestig het. Voer de test vele malen uit zonder wijzigingen. Met pytest werkt een eenvoudige shell-loop, en een plug-in zoals
pytest-repeatgeeft je--count=100. - Bewijsmateriaal vastleggen. Bewaar logboeken, schermafbeeldingen en sporen voor elke fout. De sporenviewer van Toneelschrijver is hier bijzonder nuttig.
- Benadruk de omstandigheden. Voer het uit met een beperkte CPU, parallel of in willekeurige volgorde. Door vaker te falen, kun je makkelijker studeren.
- Test het alleen. Als het altijd alleen lukt, maar mislukt in de suite, vermoed dan een gedeelde status.
- Zoek naar patronen. Controleer om welke machines, tijdstippen en aangrenzende tests het gaat.
- Controleer het product. Vraag eerlijk of dit in uw toepassing een echte raceconditie kan zijn.
Zijn automatische nieuwe pogingen een oplossing?
Nieuwe pogingen zijn een nuttig vangnet voor de korte termijn, maar geen remedie. Eén enkele automatische nieuwe poging in CI kan het werk gaande houden terwijl u onderzoek doet, maar een test waarvoor een nieuwe poging nodig is om te slagen, vertelt u nog steeds iets. Erger nog, een echte periodieke productbug die 20% van de tijd mislukt, zal een nieuwe poging meestal onopgemerkt doorstaan.
Gebruik nieuwe pogingen met deze regels: beperk ze tot één of twee, noteer elke test waarvoor er één nodig was, en behandel die lijst als uw takenlijst. Een test die wordt gerapporteerd als ‘doorgegeven bij nieuwe poging’ moet gelden als een probleem dat moet worden onderzocht, en niet als een succes.
Een teamproces voor zwakke tests
- Meten. Houd bij welke tests mislukken en geef vervolgens dezelfde commit door. Veel CI-tools en testreporters kunnen dit aantonen.
- Quarantaine. Verplaats een bevestigde zwakke test uit de blokkeringsrun, zodat deze niet langer iedereen stoort, maar blijf hem uitvoeren en volgen.
- Wijs een eigenaar en een deadline toe. Een quarantaine zonder deadline wordt gewoon een kerkhof.
- Repareren of verwijderen. Als een test het niet waard is om deze te corrigeren, verwijder deze dan en controleer of de dekking elders nog steeds bestaat.
- Voorkomen. Voer meerdere keren nieuwe tests uit voordat u samenvoegt. Een test die één keer op de vijftig runs mislukt, kun je beter nu oppakken dan over een half jaar.
Behandel een slechte test als een bug met prioriteit, en niet als achtergrondgeluid. Ernst en prioriteit zijn hier ook van toepassing, zoals beschreven in ernst versus prioriteit. Als er sprake is van een wijdverbreid probleem, lees dan meer over de bredere fouten in de testautomatisering en overweeg of u te veel tests op browserniveau heeft.
Tot slot
Schilferige tests voelen willekeurig aan, maar dat zijn ze zelden. Bijna iedereen komt neer op een aanname over tijd, orde, data of omgeving die niet altijd waar is. Kies de test die deze week het vaakst mislukt in uw pijplijn. Voer het vijftig keer uit, vind de aanname en herstel dat voorgoed. Eén echte oplossing leert een team meer dan honderd herhalingen ooit zullen doen.
Veelgestelde vragen
Wat is een vloktest?
Een zwakke test slaagt bij sommige runs en faalt bij andere zonder enige wijziging in de code. De oorzaak kan liggen in de test, de omgeving of het product.
Wat veroorzaakt schilferige tests?
De meest voorkomende oorzaken zijn timingaannames, gedeelde status tussen tests, onstabiele selectors, externe services, datumverwerking en verschillen tussen CI en lokale machines.
Moet ik schilferige tests verwijderen?
Probeer ze eerst te repareren. Als een test weinig waarde heeft en duur is om te repareren, is het prima om deze te verwijderen, maar zorg ervoor dat de belangrijke dekking ergens anders bestaat.
Moet ik mislukte tests automatisch opnieuw proberen?
Als beperkt vangnet, ja, maar registreer elke nieuwe poging en onderzoek het. Alleen al nieuwe pogingen verbergen problemen en kunnen ervoor zorgen dat echte, periodieke bugs de productie bereiken.
Hoe vind ik schilferige tests?
Zoek naar tests die mislukken en geef dezelfde commit door, met behulp van uw CI-geschiedenis of een rapportagetool. U kunt verdachte tests ook vele malen achter elkaar uitvoeren.
Blijf leren
Hulp nodig met testen, WordPress of SEO?
We helpen teams betere software te leveren en hun sites te laten groeien. Vertel ons waar u mee bezig bent en wij stellen een duidelijke volgende stap voor.
Meer handleidingen over testen, AI en moderne ontwikkeling
Genoten van dit artikel? Kies een onderwerp en ga direct naar onze nieuwste tutorials.
AI-hallucinaties uitgelegd: waarom taalmodellen dingen verzinnen en hoe ze te verminderen
AI-hallucinaties uitgelegd: wat ze zijn, waarom taalmodellen ze produceren, veelvoorkomende typen, hoe je ze kunt verminderen met aarding en aanwijz…
Lees artikel →DevOpsOmgevingsvariabelen uitgelegd: .env-bestanden, geheimen en configuratie goed gedaan
Omgevingsvariabelen uitgelegd met Python-voorbeelden die we hebben uitgevoerd: ze lezen en instellen, waarom elke waarde een string is, .env-bestand…
Lees artikel →SoftwareontwikkelingAPI-snelheidsbeperking uitgelegd: algoritmen, headers en afhandeling van 429-fouten
API-snelheidsbeperking uitgelegd met een simulatie die we hebben uitgevoerd: token-bucket, vast venster, schuifvenster, 429 reacties, headers van sn…
Lees artikel →TestautomatiseringSpotten in Python: unittest.mock, patch en de fouten die tests doen liegen
Spotten in Python met unittest.mock, getest: Mock en return_value, side_effect, patchen waar een naam wordt gebruikt, spec en autospec, spotdatums e…
Lees artikel →SoftwaretestenCodedekking uitgelegd: lijn versus filiaaldekking en wat 100% werkelijk betekent
Codedekking uitgelegd met een echte dekking.py-run: dekking van lijnen, vertakkingen en condities, een rapport lezen, waarom 100% dekking nog steeds…
Lees artikel →Databases & SQLDELETE versus TRUNCATE versus DROP in SQL: verschillen, terugdraaien en wanneer elk te gebruiken
DELETE versus TRUNCATE versus DROP uitgelegd met geteste SQL: wat elk verwijdert, terugdraaien, triggers, identiteitsreset, snelheid en hoe PostgreS…
Lees artikel →Databases & SQLSQL-weergaven uitgelegd: creëer, gebruik en vermijd de valkuilen, met voorbeelden
SQL-weergaven uitgelegd met query's die we hebben uitgevoerd: een weergave maken, live resultaten, kolommen verbergen, prestaties, bijwerkbare en ge…
Lees artikel →SoftwareontwikkelingTypeScript-interface versus type: echte verschillen, getest met de compiler
TypeScript-interface versus type, gecontroleerd met de echte compiler: wat beide kunnen doen, wat slechts één kan doen, samenvoegen van declaraties,…
Lees artikel →SoftwareontwikkelingPython *args en **kwargs uitgelegd: flexibele functies met duidelijke voorbeelden
Python *args en **kwargs uitgelegd met code die we hebben uitgevoerd: in- en uitpakken, parametervolgorde, argumenten die alleen trefwoorden en posi…
Lees artikel →SoftwareontwikkelingPython Lambda-functies: syntaxis, echt gebruik en wanneer u ze niet moet gebruiken
Python lambda-functies uitgelegd met code die we hebben uitgevoerd: syntaxis, sorteren met sleutel, kaart en filter, sluitingen, de laat-bindende va…
Lees artikel →Kunstmatige intelligentieWat zijn AI-agenten? Hoe ze werken, met een eenvoudige agentloop die u kunt uitvoeren
Wat zijn AI-agenten? Een duidelijke gids voor de agentenlus, tools, geheugen en vangrails, met een kleine Python-lus die we hebben uitgevoerd, echte…
Lees artikel →DevOpsMonitoring versus waarneembaarheid: het echte verschil, met logboeken, statistieken en sporen
Monitoring versus waarneembaarheid uitgelegd: bekende en onbekende fouten, logboeken, statistieken en sporen, een walkthrough voor afrekenincidenten…
Lees artikel →DevOpsDocker-volumes versus bindingsmounts: bewaar containergegevens op de juiste manier
Docker-volumes versus bind-mounts uitgelegd: waarom containergegevens verdwijnen, benoemde volumes, bind-mounts, tmpfs, Compose-voorbeelden, back-up…
Lees artikel →TestautomatiseringTDD versus BDD: belangrijkste verschillen, één functie die op beide manieren is gebouwd en wanneer deze moet worden gebruikt
TDD versus BDD uitgelegd met één kortingsfunctie die in beide richtingen is gebouwd: rood-groen-refactor met code die we hebben uitgevoerd, gegeven-…
Lees artikel →SoftwaretestenGebruikersacceptatietesten (UAT): proces, checklist en echte voorbeelden
Gebruikersacceptatietesten uitgelegd: wie voert het uit, hoe het verschilt van QA, een proces in zeven stappen, een UAT-testcase-sjabloon, een check…
Lees artikel →Databases & SQLSQL NULL-waarden uitgelegd: IS NULL, COALESCE, NULLIF en de te vermijden vallen
SQL NULL-waarden uitgelegd met query's die we hebben uitgevoerd: logica met drie waarden, IS NULL, COALESCE, NULLIF, hoe aggregaten, joins en sorter…
Lees artikel →Databases & SQLSQL CASE WHEN-instructie: syntaxis, echte voorbeelden en veelvoorkomende fouten
SQL CASE WHEN uitgelegd met vragen die we hebben uitgevoerd: eenvoudige en doorzochte formulieren, volgorde van de voorwaarden, draaipunten met SUM,…
Lees artikel →SoftwareontwikkelingWat is JWT? JSON-webtokens uitgelegd met een werkend voorbeeld
Wat is JWT? JSON Web Tokens uitgelegd: de drie delen, een token gebouwd en waarmee is geknoeid in de code die we hebben uitgevoerd, JWT versus sessi…
Lees artikel →SoftwareontwikkelingJavaScript-gebeurtenislus uitgelegd: call-stack, microtaken en timers met voorbeelden
De JavaScript-gebeurtenislus uitgelegd met code die we hebben uitgevoerd: call-stack, taak- en microtask-wachtrijen, waarom setTimeout 0 niet onmidd…
Lees artikel →SoftwareontwikkelingPython-generatoren en opbrengst uitgelegd: bespaar geheugen met echte voorbeelden
Python-generatoren en opbrengst uitgelegd met code die we hebben uitgevoerd: hoe ze pauzeren en hervatten, gemeten geheugenbesparingen, pijplijnen, …
Lees artikel →Kunstmatige intelligentieWat zijn inbeddingen? Vectoren en cosinusovereenkomst eenvoudig uitgelegd
Wat zijn insluitingen? Een gids in duidelijke taal over vectoren, cosinusovereenkomst en semantisch zoeken, met een klein voorbeeld dat we hebben ui…
Lees artikel →DevOpsImplementatie van blauwgroen versus kanarie: verschillen, afwegingen en hoe te kiezen
Blauw-groen versus kanarie-implementatie uitgelegd: hoe elk werkt, kosten, risico's en terugdraaien, stabiele gebruikersbucket die we hebben getest,…
Lees artikel →TestautomatiseringSchilferige tests: wat de oorzaak ervan is en hoe u ze voorgoed kunt oplossen
Zwakke tests uitgelegd met demo's die we hebben uitgevoerd: de belangrijkste oorzaken, hoe je er een kunt bevestigen en diagnosticeren, goede oploss…
Lees artikel →SoftwaretestenBelastingtesten versus stresstesten: verschillen, statistieken en k6-voorbeelden
Belastingtesten versus stresstesten uitgelegd: wat elk bewijst, piek- en soak-tests, de statistieken die er toe doen, waarom gemiddelden misleiden, …
Lees artikel →SoftwaretestenTestcase versus testscenario: belangrijkste verschillen, voorbeelden en wanneer deze te gebruiken
Testcase versus testscenario uitgelegd: duidelijke definities, een tabel naast elkaar, één functie voor het opnieuw instellen van wachtwoorden, over…
Lees artikel →Databases & SQLOptimalisatie van SQL-query's: lees uitvoeringsplannen en repareer langzame query's, met timing
Optimalisatie van SQL-query's getest op een miljoen rijen: lees uitvoeringsplannen, voeg de juiste indexen toe, repareer niet-sargeerbare filters, g…
Lees artikel →Databases & SQLSQL UNION versus UNION ALL: verschillen, voorbeelden en geteste prestaties
SQL UNION versus UNION ALL uitgelegd met de query's die we hebben uitgevoerd: duplicaten, de regels volgen beide, een echte databug, gemeten prestat…
Lees artikel →SoftwareontwikkelingJavaScript Fetch API-tutorial: GET, POST, foutafhandeling en time-outs
Een JavaScript Fetch API-tutorial getest tegen een lokale server: GET en POST, waarom 404 niet gooit, een herbruikbare helper, queryreeksen, time-ou…
Lees artikel →SoftwareontwikkelingPython-bestandsverwerking: lezen, schrijven, CSV, JSON en pathlib met voorbeelden
Het verwerken van Python-bestanden uitgelegd met code die we hebben uitgevoerd: open modi, met het lezen van grote bestanden, CSV, JSON, pathlib, bi…
Lees artikel →SoftwareontwikkelingPython-klassen en -objecten: OOP-zelfstudie met echte voorbeelden
Python-klassen en -objecten uitgelegd met code die we hebben uitgevoerd: __init__, methoden, klasse versus instantie-attributen, overerving, eigensc…
Lees artikel →Kunstmatige intelligentieWat is een groot taalmodel? Hoe LLM's werken, uitgelegd zonder de hype
Wat is een groot taalmodel? LLM's uitgelegd in duidelijke taal: tokens, inbedding, aandacht, training, temperatuur- en contextvensters, een kleine d…
Lees artikel →SoftwareontwikkelingGit merge vs rebase: verschillen, voorbeelden en wanneer je wat gebruikt
Git merge vs rebase uitgelegd met echte logoutput, een vergelijkingstabel, de gouden regel van rebase en een veilige teamwerkwijze.
Lees artikel →SoftwareontwikkelingPython list comprehension: syntaxis, voorbeelden en wanneer je het vermijdt
Python list comprehension uitgelegd: syntaxis, filteren, if-else, geneste lussen, gemeten tijden en wanneer een gewone for-lus beter is.
Lees artikel →Databases & SQLSQL JOIN uitgelegd met voorbeelden (Inner, Left, Right, Full)
Begrijp SQL joins stap voor stap met voorbeeldtabellen en queries: INNER, LEFT, RIGHT, FULL, CROSS en SELF, plus veelgemaakte fouten en prestatietips.
Lees artikel →