Cypress- en schilferige tests:omgaan met time-outfouten?

Cypress is een geautomatiseerd end-to-end testraamwerk met meer dan drie miljoen wekelijkse open source downloads op het moment van schrijven. De constante populariteit is niet zonder reden; de voordelen van het gebruik van Cypress zijn onder andere een tool voor het visualiseren van snapshots, automatisch herladen na elke wijziging in uw tests en de mogelijkheid om netwerkverzoeken en -reacties te beheren zonder ooit uw server te raken.

Ik heb genoten van de voordelen die Cypress biedt na het integreren van deze tool in een aantal projecten, maar zoals bij elke nieuwe technologie zijn er bepaalde leercurves die moeten worden overwonnen. Een leermogelijkheid die ik onlangs zag, betrof een lokaal geslaagde testsuite die ook falende Cypress-tests op CI produceerde. Dit probleem stuurde me in een Stack Overflow-konijnenhol, maar sindsdien ben ik naar voren gekomen met een hernieuwde wijsheid.

Hieronder vindt u een overzicht van deze informatie, inclusief een korte beschrijving van wat schilferige tests zijn, hoe ze ontstaan ​​en hoe u schilferige Cypress-tests kunt aanpakken wanneer ze lokaal of in uw CI-pijplijn verschijnen.

Wat is een schilferige test?

De term 'schilferige test' is een algemene term die van toepassing kan zijn op elke test die in elk toetsingskader is geschreven. Een test wordt als schilferig beschouwd als deze u inconsistente resultaten geeft bij verschillende runs, zelfs als u geen wijzigingen in uw testcode heeft aangebracht. Je weet dat je een schilferige test hebt wanneer je je testsuite uitvoert en in eerste instantie een voldoende haalt, maar dezelfde test mislukt bij een volgende run (of vice versa).

Schilferige tests voelen willekeurig aan omdat de reden voor hun inconsistentie niet meteen duidelijk is. Aangezien uw testcode niet is gewijzigd, moet er achter de schermen iets anders aan de hand zijn, en het vinden van dit probleem kan vaak lastig zijn. Afhankelijk van het testkader dat u gebruikt, zijn er echter enkele veelvoorkomende boosdoeners voor schilferige tests, en het genezen van de uwe kan een eenvoudig proces van eliminatie vereisen.

Voor gebruikers van Cypress en andere end-to-end testraamwerken is uw schilferige test hoogstwaarschijnlijk het resultaat van een van de volgende zaken:

  • De aanwezigheid van animaties op uw gebruikersinterface
  • De betreffende schilferige test is niet voldoende geïsoleerd van andere tests in uw testsuite
  • De applicatiestatus die nodig is om een ​​bepaalde test te doorstaan, is niet correct ingesteld voordat de test wordt uitgevoerd
  • Asynchrone bewerkingen worden niet voltooid voordat Cypress een opdracht uitvoert, wat een time-outfout veroorzaakt

Schilferige Cypress-tests veroorzaakt door time-outfouten

Het bleek dat mijn Cypress-testsuite CI-storingen een time-outprobleem met zich meebrachten. Over het algemeen kan een "time-out" optreden wanneer een programma niet binnen een bepaalde tijd een reactie ontvangt, wat resulteert in een fout.

In de context van het testen van een webtoepassing kan een time-outfout optreden wanneer de app een asynchrone bewerking uitvoert die moet worden voltooid voordat de toepassingsstatus en/of gebruikersinterface gereed zijn om te worden getest. Als een Cypress-opdracht of -bevestiging wordt uitgevoerd voordat deze bewerking is voltooid, zal uw test waarschijnlijk mislukken. Als de tijd die nodig is om deze bewerking te voltooien echter fluctueert, kan deze soms ook binnen voldoende tijd worden voltooid om een ​​voldoende test te produceren. Zoals je je kunt voorstellen, is dit een perfect recept voor het maken van een schilferige test.

Hoe Cypress anticipeert op schilferige tests

Gelukkig biedt Cypress een aantal standaardgedragingen om te anticiperen op "de asynchrone aard van webtoepassingen", evenals andere opties die ontwikkelaars handmatig kunnen gebruiken om aan de specifieke behoeften van hun toepassing te voldoen. Een voorbeeld van zo'n standaardgedrag is dat u automatisch vier seconden moet wachten (idealiter om uw toepassing de bewerking te laten voltooien die deze mogelijk verwerkt) voordat een time-out wordt bereikt. Ontwikkelaars kunnen ervoor kiezen om deze standaard te negeren met een willekeurig aantal time-outdeclaraties, hetzij binnen een specifieke test, een reeks tests, of als onderdeel van uw globale configuratie.

cy.get('[data-cy=input-box]', { timeout: 10000 }).type('Input');
cy.get('[data-cy=submit-button]', { timeout: 7000 }).click();
cy.get('[data-cy=input-box]', { timeout: 5000 }).should('not.have.value');

The example above displays three cypress.get() commands with individual timeout specifications for each. Since Cypress sets timeouts in milliseconds, Cypress would wait 10 seconds, 7 seconds, and 5 second before looking for each associated element and executing the subsequent commands and assertions in this example, respectively.

Ontwikkelaars kunnen er ook voor kiezen om nieuwe tests in te schakelen in hun globale configuraties. Dit zal Cypress ertoe aanzetten om mislukte tests zo vaak opnieuw te proberen als de ontwikkelaar opgeeft.

"requestTimeout": 2000,
"defaultCommandTimeout": 5000,
"retries": 3

The example above displays global configuration options within the cypress.json file. The first two will override Cypress default timeout settings, while the “retries” option specifies how many times Cypress should retry failed tests before moving on to the remainder of the test suite.

Ten slotte biedt Cypress ook een schilferige testdetectiefunctie op het Cypress Dashboard. Als de optie "testpogingen" is ingeschakeld, zal deze functie alle schilferige tests in uw testsuite markeren en analyses bieden over het aantal en de ernst van deze tests in de loop van de tijd. Het is belangrijk op te merken dat deze functies alleen toegankelijk zijn voor ontwikkelaars die deel uitmaken van een Cypress Team Dashboard-abonnement. Bij afwezigheid van deze functies moeten ontwikkelaars hun testsuite meerdere keren uitvoeren zonder wijzigingen in hun code aan te brengen om te bepalen of deze schilferige tests bevat.

Manieren om schilferige tests, veroorzaakt door time-outfouten, handmatig aan te pakken

Na het identificeren van schilferige tests in mijn testsuite, heb ik mijn codebase aangepast om de time-outfouten op te vangen die inconsistente resultaten veroorzaakten. Echter, na het pushen van mijn wijzigingen naar de remote branch, zag ik nu dat mijn testspecificaties lokaal doorgingen, maar faalden op CI. Nadat ik met de hoofdtak had gerebaseerd en nog steeds falende tests op CI zag, begon ik te zoeken naar meer oplossingen die de time-outproblemen van Cypress-tests aanpakken.

De volgende lijst geeft een aantal opties weer die beschikbaar zijn voor ontwikkelaars die vergelijkbare Cypress-fouten ervaren, een combinatie waarvan ik heb gebruikt om een ​​succesvolle build te leveren.

Vereisen dat Cypress wacht tot een netwerkverzoek is voltooid

Als uw schilferige test het resultaat is van het uitvoeren van opdrachten en beweringen door Cypress voordat een noodzakelijk netwerkverzoek is voltooid, kunt u dat verzoek onderscheppen en Cypress laten wachten tot het is voltooid voordat het verdere opdrachten uitvoert.

Om dit te bereiken, begint u met het definiëren van de onderschepte route en het toewijzen van een alias. Deze alias kan dan later worden opgeroepen, wanneer het antwoord op dat verzoek nodig is voor testdoeleinden. U kunt dan een callback-functie gebruiken die de Cypress-commando's en -beweringen uitvoert die integraal deel uitmaken van uw test.

cy.intercept('GET', '/api/v1/candidate/assessment-attempt*', {
  fixture: 'candidate/stubbedAssessments.json'
}).as('getActiveAssessments');

it('meets default question settings', () => {
  cy.wait('@getActiveAssessments').then(() => {
    cy.get('[data-cy=start-assessment-button]').should('exist');
  });
});

The example above displays an intercepted network request with a specified method and route. This particular interception also stubs the response that this network request would have otherwise provided to our test, instead producing mock data found in the associated fixture file. Lastly, this interception is given an alias, getActiveAssessments, through use of the .as() command. The subsequent test in this code snippet then accesses this alias and requires Cypress to wait on its response before executing anything found in the following callback function.

Alle netwerkverzoeken onderscheppen om responstijden te beheersen

In sommige situaties kan het zinvol zijn om tijdens het testproces netwerkverzoeken naar uw server te sturen om live gegevens op te halen. Als u dit doet, stelt u uw testomgeving echter open voor een paar externe variabelen die moeilijker te controleren zijn. Als uw server niet beschikbaar is, of als de reactietijd varieert, of als er meerdere verzoeken tegelijk zijn, ziet u mogelijk schilferige tests in uw testsuite. Het onderscheppen van alle relevante netwerkverzoeken in een bepaalde specificatie en het verstrekken van uw eigen nepgegevens als antwoord, kan de variabele aard van dit netwerkverkeer verminderen. In plaats van te wachten op een reactie van uw server, kan Cypress snel uw nepgegevens pakken en doorgaan met het uitvoeren van uw tests.

Breek uw testsuite op in kleinere specificaties

Een andere manier om time-outfouten af ​​te handelen die schilferige tests veroorzaken, is het verkleinen van grote specificatiebestanden. Lange specificatiebestanden zijn niet alleen moeilijk te onderhouden, ze kunnen het opsporen van de oorzaak van een schilferige test ook ingewikkelder maken. Dit is met name het geval als de applicatiestatus niet correct is ingesteld binnen een test, en ook niet wordt opgeschoond nadat een test is voltooid, omdat deze factoren de daaropvolgende tests in uw testsuite kunnen beïnvloeden, waardoor er meer fouten optreden. Als dit het geval is voor meerdere tests in een lang specificatiebestand, kan het zijn dat je een spelletje mep speelt, waarbij het aanpassen van de ene test leidt tot een mislukking in de andere.

In de context van time-outfouten hebben kleinere specificatiebestanden het voordeel dat ze het netwerkverkeer beperken dat nodig is om uw tests correct uit te voeren. Deze beperking op zich kan u een beter begrip geven van wat er precies in uw toepassing gebeurt op het moment dat uw test plaatsvindt, en wat u moet controleren om een ​​geslaagde test te schrijven.

Tegelijkertijd betekent het opsplitsen van brokken gerelateerde tests in hun eigen onafhankelijke specificatiebestand dat deze tests worden geïsoleerd van onnodige processen die plaatsvonden in de grotere testsuite. Als u een kleiner aantal tests en processen hoeft uit te voeren, kunt u de oorzaak van uw schilferige tests beter lokaliseren door middel van een eliminatieproces.

Vereisen dat Cypress een willekeurig aantal seconden wacht

De laatste optie in deze lijst is het gebruik van de cy.wait() commando om handmatig op te geven hoeveel seconden Cypress moet wachten op een bepaald punt in uw testbestand. Deze oplossing is eenvoudig, maar niet helemaal betrouwbaar, dus je moet het beschouwen als een soort laatste redmiddel of snelle oplossing; hoewel je Cypress misschien lang genoeg kunt pauzeren om een ​​time-outfout te voorkomen, is dit resultaat niet altijd gegarandeerd, vooral als je applicatie groeit om later nieuwe functies en gedragingen te introduceren. Tegelijkertijd kan het implementeren van een willekeurig wachtcommando ook helemaal niet nodig zijn. U kunt bijvoorbeeld per ongeluk pauzeren om te wachten op het voltooien van een bewerking die al is voltooid.

cy.wait(10000);

The above command requires Cypress to wait 10 seconds before moving on to the subsequent code in a spec file.

Gebruik cy.wait() om een ​​willekeurig aantal seconden op te geven voor Cypress om te wachten kan in sommige contexten nog steeds nuttig zijn. Als uw toepassing relatief klein is, of als uw spec-bestanden en tests voldoende geïsoleerd zijn, kan het risico van het implementeren van een onnodige of onbetrouwbare wachtopdracht klein genoeg zijn om het gebruik ervan te rechtvaardigen. Het kan echter zijn dat u andere alternatieven wilt uitputten voordat u naar deze oplossing gaat, omdat te veel van deze opdrachten de runtime van uw testsuite kunnen doen opzwellen en in feite kunnen wijzen op een dieper probleem in uw testsuite of webtoepassing.

Belangrijkste afhaalrestaurants

  • Een schilferige test is een test die inconsistente resultaten oplevert ondanks het feit dat er geen wijzigingen in de testcode zijn aangebracht tussen testruns.

  • Soms zijn schilferige Cypress-tests het resultaat van time-outfouten; een asynchroon proces in uw applicatiecode kan worden voltooid voor of nadat Cypress een bepaalde bewering heeft getest, wat tot inconsistente resultaten leidt.

  • Cypress biedt enkele standaard time-outinstellingen om te anticiperen op asynchrone processen in uw applicatiecode. Als deze standaardbeveiligingen niet werken, kunnen ontwikkelaars ervoor kiezen ze te negeren binnen hun testcode of binnen hun algemene configuraties.

  • Ontwikkelaars kunnen ervoor kiezen om schilferige tests, veroorzaakt door time-outfouten, handmatig aan te pakken door een of een combinatie van het volgende te volgen:

    • Gebruik de onderscheppings- en aliasing-opdrachten van Cypress om Cypress te laten wachten tot uw asynchrone bewerkingen zijn voltooid voordat u de volgende opdracht of bewering uitvoert.
    • Gebruik de opdracht Cypress intercept om al het netwerkverkeer te beheren dat nodig is voor uw tests om inconsistenties tussen testruns te elimineren.
    • Breek uw testpakket op in kleinere specificaties om het aantal asynchrone bewerkingen waarop uw tests vertrouwen te beperken en om de oorzaak van schilferige tests sneller te vinden.
    • Gebruik cy.wait() commando om Cypress handmatig een bepaald aantal seconden te laten wachten voordat een bepaalde test wordt uitgevoerd.

Conclusie

Alle manieren om een ​​schilferige Cypress-test aan te pakken, kunnen waarschijnlijk een heel dik boek vullen. Hopelijk kunnen enkele van de hier uiteengezette opties helpen bij het oplossen van uw probleem of u in de goede richting wijzen.

Dit artikel is voor het eerst gepubliceerd op shipshape.io.