Schilferige tests: wat de oorzaak ervan is en hoe u ze voorgoed kunt oplossen

  1. Home
  2. Blog
  3. Testautomatisering

Testautomatisering

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.

9 min leestijd
Vlokkige tests: wat veroorzaakt ze en hoe kun je ze voorgoed oplossen?

Belangrijkste punten

  • 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

  1. Bevestig het. Voer de test vele malen uit zonder wijzigingen. Met pytest werkt een eenvoudige shell-loop, en een plug-in zoals pytest-repeat geeft je --count=100.
  2. Bewijsmateriaal vastleggen. Bewaar logboeken, schermafbeeldingen en sporen voor elke fout. De sporenviewer van Toneelschrijver is hier bijzonder nuttig.
  3. Benadruk de omstandigheden. Voer het uit met een beperkte CPU, parallel of in willekeurige volgorde. Door vaker te falen, kun je makkelijker studeren.
  4. Test het alleen. Als het altijd alleen lukt, maar mislukt in de suite, vermoed dan een gedeelde status.
  5. Zoek naar patronen. Controleer om welke machines, tijdstippen en aangrenzende tests het gaat.
  6. 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

  1. Meten. Houd bij welke tests mislukken en geef vervolgens dezelfde commit door. Veel CI-tools en testreporters kunnen dit aantonen.
  2. Quarantaine. Verplaats een bevestigde zwakke test uit de blokkeringsrun, zodat deze niet langer iedereen stoort, maar blijf hem uitvoeren en volgen.
  3. Wijs een eigenaar en een deadline toe. Een quarantaine zonder deadline wordt gewoon een kerkhof.
  4. 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.
  5. 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

Werk met Stack Quora

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.

Blijf lezen

Meer handleidingen over testen, AI en moderne ontwikkeling

Genoten van dit artikel? Kies een onderwerp en ga direct naar onze nieuwste tutorials.

Kunstmatige intelligentie

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 →
DevOps

Omgevingsvariabelen 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 →
Softwareontwikkeling

API-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 →
Testautomatisering

Spotten 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 →
Softwaretesten

Codedekking 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 & SQL

DELETE 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 & SQL

SQL-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 →
Softwareontwikkeling

TypeScript-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 →
Softwareontwikkeling

Python *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 →
Softwareontwikkeling

Python 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 intelligentie

Wat 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 →
DevOps

Monitoring 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 →
DevOps

Docker-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 →
Testautomatisering

TDD 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 →
Softwaretesten

Gebruikersacceptatietesten (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 & SQL

SQL 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 & SQL

SQL 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 →
Softwareontwikkeling

Wat 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 →
Softwareontwikkeling

JavaScript-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 →
Softwareontwikkeling

Python-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 intelligentie

Wat 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 →
DevOps

Implementatie 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 →
Testautomatisering

Schilferige 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 →
Softwaretesten

Belastingtesten 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 →
Softwaretesten

Testcase 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 & SQL

Optimalisatie 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 & SQL

SQL 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 →
Softwareontwikkeling

JavaScript 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 →
Softwareontwikkeling

Python-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 →
Softwareontwikkeling

Python-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 intelligentie

Wat 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 →
Softwareontwikkeling

Git 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 →
Softwareontwikkeling

Python 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 & SQL

SQL 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 →