Node.js & JavaScript Testing Best Practices (2019)

Viktig merknad: Denne artikkelen ble et GitHub-lager med ytterligere 15 beste praksis og samfunnsdiskusjoner

Kort introduksjon

Som en uavhengig Node.js-konsulent er jeg engasjert og gjennomgår 10+ prosjekter hvert år, og kundene mine ber med rette om å fokusere på testing. For noen måneder siden begynte jeg å dokumentere innsikten og gjenta feilene jeg observerte på feltet, og plutselig ble det samlet opp i 30 tester av beste praksis.

Ideene nedenfor spenner over temaer som å velge riktige testtyper, koding av dem riktig, måling av effektiviteten og hosting av dem i en CI / CD-rørledning på riktig måte. Noen eksempler er illustrert ved bruk av Jest, andre med Mocha - dette innlegget handler mindre om verktøy og mer om riktig tilnærming og teknikker.

Jeg heter Yoni Goldberg, en uavhengig Node.JS-konsulent og medforfatter av Node.js beste praksis. Jeg jobber med kunder i USA, Europa og Israel for å polere Node.js-applikasjonene. Blant tjenestene mine er også testplanlegging, testgjennomgang og CI / CD-oppsett. Følg meg på Twitter ️

Gjennomgått og forbedret av Bruno Scheufler

️ 0. Den gylne regelen: Testingen må forbli død - enkel og klar som dagen

Er du kjent med den smileyvennen, familiemedlemmet, eller kanskje en filmkarakter som alltid er tilgjengelig for å skåne de gode arbeidshendene hans, som hjelper deg hele døgnet når du trenger ham innstilt på positiv energi, men som likevel ber så lite om seg selv? Slik skal en testkode designes - enkelt, verdifullt og morsomt. Dette kan oppnås ved selektivt kirsebærplukkingsteknikker, verktøy og testmål som er kostnadseffektive og gir god avkastning. Test bare så mye som nødvendig, forsøk å holde det smidig, noen ganger er det til og med verdt å slippe noen tester og handelssikkerhet for smidighet og enkelhet.

Testing skal ikke behandles som en tradisjonell applikasjonskode - et typisk team utfordres med å opprettholde hovedprogrammet uansett (funksjonene vi koder og selger), det tåler ikke ytterligere komplekst "prosjekt". Skulle testing vokse til å være en ekstra kilde til smerte - vil den bli forlatt eller redusere utviklingen kraftig.

I den forstand må testkoden forbli død-enkel, med minimale avhengigheter, abstraksjoner og nivåer av indireksjon. Man bør se på en test og få intensjonen øyeblikkelig. De fleste av rådene nedenfor er derivater av dette prinsippet

*** Avsnitt : Testanatomi ***

️ 1. Ta med 3 deler i hvert testnavn

Gjør: En testrapport skal fortelle om den gjeldende programrevisjonen tilfredsstiller kravene til personene som ikke nødvendigvis er kjent med koden: testeren, DevOps-ingeniøren som distribuerer og fremtiden du om to år fra nå. Dette oppnås best hvis testene snakker på kravnivå og inkluderer tre deler:

(1) Hva blir testet? For eksempel metoden ProductsService.addNewProduct

(2) Under hvilke omstendigheter og scenario? For eksempel blir ingen pris ført til metoden

(3) Hva er forventet resultat? For eksempel er det nye produktet ikke godkjent

Ellers: En distribusjon mislyktes, en test som heter “Legg til produkt” mislyktes. Forteller dette deg hva som egentlig fungerer?

Gjør det riktig Eksempel: Et testnavn som utgjør 3 deler

️☺ Gjør det riktig eksempel: Testrapporten ligner kravdokumentet

Ta med 3 deler i hvert testtilfelle, snakk produktspråket

️ 2. Beskriv forventningene på et produktspråk: bruk påstander i BDD-stil

Gjør: Ved å kode testene dine i en erklærende stil, kan leseren få taket umiddelbart uten å bruke en eneste hjerne-CPU-syklus. Når du skriver en nødvendig kode som er fullpakket med betinget logikk, blir leseren kastet bort til en innsatsfull mental stemning. I den forstand kan du kode forventningen på et menneskelignende språk, deklarativ BDD-stil ved å bruke forvent eller bør og ikke bruke tilpasset kode. Hvis Chai & Jest ikke inkluderer ønsket påstand og det er veldig repeterbart, kan du vurdere å utvide Jest matcher (Jest) eller skrive en tilpasset Chai-plugin

Ellers: Teamet vil skrive mindre test og pynte de irriterende med .skip ()

Antimønstereksempel: Leseren må bla gjennom ikke så kort og tvingende kode bare for å få testhistorien

Gjør det riktig eksempel: Skimming gjennom følgende deklarative test er en lek

️ 3. Lint med test-dedikerte plugins

Gjør: Et sett med ESLint-plugins ble bygget spesielt for å inspisere testkodemønstrene og oppdage problemer. For eksempel vil eslint-plugin-mokka advare når en test er skrevet på globalt nivå (ikke en sønn av en beskriv () -utsagn) eller når tester hoppes over, noe som kan føre til en falsk tro på at alle testene er bestått. På samme måte kan eslint-plugin-jest for eksempel advare når en test ikke har noen påstander i det hele tatt (ikke sjekker noe)

Ellers: Å se 90% kodedekning og 100% grønne tester vil få ansiktet til å bære et stort smil bare til du innser at mange tester ikke hevder for noe og mange testsuiter bare ble hoppet over. Forhåpentligvis distribuerte du ikke noe basert på denne falske observasjonen

Antimønstereksempel: En testsak full av feil, heldigvis blir alle fanget av Linters

️ 4. Hold deg til svartboks-testing: Test bare offentlige metoder

Gjør: Testing av internene gir stort overhead for nesten ingenting. Hvis koden / API-en din gir riktige resultater, bør du virkelig investere de neste 3 timene dine i testing HVORDAN den fungerte internt og deretter opprettholde disse skjøre testene? Når en offentlig oppførsel blir sjekket, blir den private implementeringen også implisitt testet, og testene dine vil bare gå i stykker hvis det er et visst problem (f.eks. Feil utdata). Denne tilnærmingen omtales også som atferdstesting. På den andre siden, bør du teste internalene (hvitboks-tilnærming) - fokuset ditt skifter fra å planlegge komponentutfallet til nitty-gritty detaljer, og testen din kan gå i stykker på grunn av mindre kodefaktorer, selv om resultatene er fine - dette øker vedlikeholdet dramatisk byrde

Ellers: Testen din oppfører seg som barnet som gråter ulv: skyter høyt falsk-positive rop (f.eks. En test mislykkes fordi et privat variabelnavn ble endret). Overraskende vil folk snart begynne å ignorere CI-varslene til en dag en ekte feil vil bli ignorert ...

Antimønstereksempel: En testsak tester internene uten god grunn

️️5. Velg riktig testdobling: Unngå spott til fordel for stubber og spioner

Gjør: Testdubbler er et nødvendig onde fordi de er koblet til applikasjonsinterne, men noen gir en enorm verdi (Les her en påminnelse om testdobbel: mock vs stubber vs spioner). Imidlertid ble de forskjellige teknikkene ikke født likeverdige: Noen av dem, spioner og stubber, er fokusert på å teste kravene, men som en uunngåelig bivirkning berører de også litt på internalene. Motsetning, på motsatt side, er fokusert på å teste internalene - dette gir enormt overhead som forklart i kulen “Stick to black box testing”.

Før du bruker testdubletter, kan du stille et veldig enkelt spørsmål: Bruker jeg det til å teste funksjonalitet som vises, eller kan vises, i kravdokumentet? Hvis nei, er det en lukt av testing av hvite bokser.

Hvis du for eksempel vil teste hva appen din oppfører seg rimelig når betalingstjenesten er nede, kan det hende du stopper betalingstjenesten og utløser litt "No Response" -retur for å sikre at enheten under test returnerer riktig verdi. Dette sjekker applikasjonsatferden / responsen / utfallet under visse scenarier. Du kan også bruke en spion for å hevde at en e-post ble sendt når denne tjenesten er nede - dette er igjen en atferdskontroll som sannsynligvis vil vises i et krav-dokument ("Send en e-post hvis betaling ikke kunne lagres"). På baksiden, hvis du håner betalingstjenesten og forsikrer deg om at den ble kalt med riktige JavaScript-typer - så er testen din fokusert på interne ting som ikke har noe med applikasjonsfunksjonaliteten og vil sannsynligvis endre seg ofte

Ellers: Eventuell refactoring av kodemandater som søker etter alle mockene i koden og oppdateres deretter. Tester blir en byrde i stedet for en hjelpsom venn

Antimønstereksempel: Spotter fokus på internene

Gjør det riktig Eksempel: spioner er fokusert på å teste kravene, men som en bivirkning er det uunngåelig å berøre internalen

️ 6. Ikke "foo", bruk realistiske inputdata

Gjør: Ofte blir produksjonsfeil avslørt under noen veldig spesifikke og overraskende innspill - jo mer realistisk testinnspillet er, jo større er sjansen for å fange feil tidlig. Bruk dedikerte biblioteker som Faker til å generere pseudo-reelle data som ligner på mangfold og form for produksjonsdata. For eksempel vil slike biblioteker generere tilfeldige, men realistiske telefonnummer, brukernavn, kredittkort, firmanavn og til og med ‘lorem ipsum’ -tekst. Vurder å importere ekte data fra produksjonsmiljøet ditt og bruk det i testene dine. Vil du ta det til neste nivå? se neste kule (eiendomsbasert testing)

Ellers: All utviklingstesting vil feilaktig virke grønn når du bruker syntetiske innganger som “Foo”, men da kan produksjonen bli rød når en hacker passerer en ekkel streng som “@ 3e2ddsf. ## '1 fdsfds. fds432 AAAA ”

Antimønstereksempel: En testsuite som passerer på grunn av ikke-realistiske data

☺Doing It Right Eksempel: Tilfeldig realistiske innspill

7. Test mange inngangskombinasjoner ved hjelp av eiendomsbasert testing

Gjør: Vanligvis velger vi noen få inputprøver for hver test. Selv når inndataformatet ligner data fra den virkelige verden (se kule 'Ikke foo'), dekker vi bare noen få inngangskombinasjoner (metode ('', sann, 1), metode («streng», falsk », 0) ), Imidlertid kan en API som kalles med 5 parametere, i produksjonen påberopes med tusenvis av forskjellige permutasjoner, en av dem kan gjøre prosessen vår nede (se Fuzz Testing). Hva om du kunne skrevet en enkelt test som automatisk sender 1000 permutasjoner av forskjellige innganger og fanger for hvilke innspill koden vår ikke klarer å gi riktig svar? Eiendomsbasert testing er en teknikk som gjør akkurat det: ved å sende alle mulige inngangskombinasjoner til enheten din som testes, øker det serendipiteten til å finne en feil. For eksempel, gitt en metode - addNewProduct (id, name, isDiscount) - vil de støttende bibliotekene kalle denne metoden med mange kombinasjoner av (nummer, streng, boolsk) som (1, "iPhone", falsk), (2, "Galaxy" ”, Sant). Du kan kjøre eiendomsbasert testing ved å bruke din favoritt testløper (Mocha, Jest osv.) Ved å bruke biblioteker som js-verifisere eller testcheck (mye bedre dokumentasjon). Oppdatering: Nicolas Dubien foreslår i kommentarene nedenfor å sjekke hurtigsjekk som ser ut til å tilby noen tilleggsfunksjoner og også å opprettholdes aktivt

Ellers: Ubevisst velger du testinngangene som bare dekker kodeveier som fungerer bra. Dessverre reduserer dette effektiviteten av testing som et kjøretøy for å eksponere feil

☺ Gjør det riktig eksempel: Testing av mange inngangs permutasjoner med "mokka-testsjekk"

️ 8. Hold deg innenfor testen: Minimer eksterne hjelpere og abstraksjoner

Gjør: Nå er det sannsynligvis åpenbart at jeg tar til orde for døde enkle tester: Teamet har ikke råd til et annet programvareprosjekt som krever en mental innsats for å forstå koden. Michael Lync forklarer dette i sitt flotte innlegg:

God produksjonskode er godt innarbeidet; god testkode er åpenbar ... Når du skriver en test, kan du tenke på den neste utvikleren som vil se testbruddet. De ønsker ikke å lese hele testserien din, og de ønsker absolutt ikke å lese et helt arveliv av testverktøy.

La leseren få hele historien uten å forlate testen, minimer redskaper, kroker eller noen ytre effekt på en testkoffert. For mange repetisjoner og kopi-liming? OK, en test kan gå med en ekstern hjelper og være åpenbar. Men når det vokser til tre og fire hjelpere og kroker, innebærer det at det langsomt dannes en kompleks struktur

Ellers: Plutselig fant du deg selv med 4 hjelpere per testsuite, hvorav to av dem arvet fra base util, mye oppsett og rivkroker? gratulerer, har du nettopp vunnet et annet utfordrende prosjekt å opprettholde, kan du skrive tester snart mot testpakken

Antimønstereksempel: Fancy og indirekte teststruktur. Forstår du test saken uten å navigere til eksterne avhengigheter?

☺ Gjør det riktig Eksempel: En test du kanskje forstår uten å hoppe gjennom forskjellige filer

️ 9. Unngå globale testarmaturer og frø, legg til data per test

Gjør: Etter den gyldne regelen (kule 0), bør hver test legge til og handle på sitt eget sett med DB-rader for å forhindre kobling og enkelt resonnere om teststrømmen. I virkeligheten blir dette ofte krenket av testere som frø DB med data før de kjører testene (også kjent som ‘testarmatur’) av hensyn til ytelsesforbedring. Selv om ytelse virkelig er en gyldig bekymring - den kan avbøtes (se “Komponenttesting” -kule), er testkompleksiteten imidlertid en mye smertefull sorg som bør styre andre hensyn mesteparten av tiden. Praktisk sett, gjør at hver testsak eksplisitt legger til DB-postene den trenger, og handler bare i henhold til disse postene. Hvis ytelse blir en kritisk bekymring - kan et balansert kompromiss komme i form av å se på den eneste pakken med tester som ikke muterer data (f.eks. Spørsmål)

Ellers: Få tester mislykkes, en distribusjon blir avbrutt, teamet vårt kommer til å bruke dyrebar tid nå, har vi en feil? la oss undersøke, nei, det virker som om to tester muterte de samme frøopplysningene

Antimønstereksempel: testene er ikke uavhengige og er avhengige av en global krok for å mate globale DB-data

☺Doing It Right Eksempel: Vi kan holde oss innenfor testen, hver test fungerer på sitt eget datasett

️ 10. Ikke ta feil, forvent dem

Gjør: Når du prøver å hevde at noen innspill utløser en feil, kan det se riktig ut å bruke try-catch-endelig og hevde at fangstklausulen var lagt inn. Resultatet er en vanskelig og ordinær testsak (eksempel nedenfor) som skjuler den enkle testintensjonen og resultatforventningene

Et mer elegant alternativ er å bruke den enlinje dedikerte Chai-påstanden: forvent (metode) .to.kast (eller i Jest: forventer (metoden) .toTrow ()). Det er absolutt obligatorisk å også sikre at unntaket inneholder en egenskap som forteller feiltypen, ellers gitt bare en generell feil vil ikke applikasjonen kunne gjøre mye fremfor å vise en skuffende melding til brukeren

Ellers: Det vil være utfordrende å utlede fra testrapportene (f.eks. CI-rapporter) hva som gikk galt

Antimønstereksempel: En lang testtilfelle som prøver å hevde at det foreligger feil med prøvefangst

☺Doing It Right Eksempel: En menneskelig lesbar forventning som lett kunne forstås, kanskje til og med av QA eller teknisk statsminister

️10. Merk testene dine

Gjør: Ulike tester må kjøres i forskjellige scenarier: hurtigrøyk, IO-mindre, tester skal kjøres når en utvikler lagrer eller begår en fil, fullstendige tester som alltid slutter når en ny pull-forespørsel sendes inn, etc. Dette kan oppnås ved å tagge tester med nøkkelord som #cold #api #sanity slik at du kan grep med testselen og påkalle ønsket undergruppe. For eksempel er det slik du bare vil påberope deg mentalitetstestgruppen med Mokka: mokka - grep ‘fornuft’

Ellers: Å kjøre alle testene, inkludert tester som utfører dusinvis av DB-spørsmål, når som helst en utvikler gjør en liten endring kan være ekstremt treg og holder utviklere borte fra å kjøre tester

Gjør det riktig eksempel: Merking av tester som ‘# kaldtest’ gjør at testløperen kun kan utføre raske tester (Cold === raske tester som ikke gjør noe IO og kan utføres ofte selv når utvikleren skriver)

11. Annen generisk god testhygiene

Gjør: Dette innlegget er fokusert på testing av råd som er relatert til, eller i det minste kan eksemplifiseres med Node JS. Denne kulen grupperer imidlertid få ikke-nodrelaterte tips som er velkjente

Lær og praktiser TDD-prinsipper - de er ekstremt verdifulle for mange, men blir ikke skremt hvis de ikke passer til stilen din, du er ikke den eneste. Vurder å skrive testene før koden i en rød-grønn-refaktor stil, sørg for at hver test sjekker nøyaktig en ting, når du finner en feil - før du fikser, skriv en test som vil oppdage denne feilen i fremtiden, la hver test mislykkes minst en gang før du blir grønn, unngå miljøavhengighet (stier, operativsystem osv.)

Ellers: Du vil savne visdomperler som ble samlet inn i flere tiår

*** Avsnitt : Testtyper ***

️ 12. Berik testporteføljen din: Se utover enhetstester og pyramiden

Gjør: Testpyramiden, selv om den er 10 år gammel, er en flott og relevant modell som antyder tre testtyper og påvirker de fleste utviklers teststrategi. Samtidig dukket det opp mer enn en håndfull skinnende nye testteknikker og gjemmer seg i skyggene av testpyramiden. Gitt alle de dramatiske endringene vi har sett de siste ti årene (Microservices, cloud, serverless), er det til og med mulig at en ganske gammel modell vil passe til * alle * typer applikasjoner? bør ikke testverden vurdere å ta imot nye testteknikker?

Ikke misforstå, i 2019 er testpyramiden, TDD og enhetstestene fortsatt en kraftig teknikk og er sannsynligvis den beste kampen for mange applikasjoner. Bare som alle andre modeller, til tross for nytten, må det være feil noen ganger. Tenk for eksempel på et IOT-program som inntar mange hendelser i en meldingsbuss som Kafka / RabbitMQ, som deretter flyter inn i et datavarehus og til slutt spørres av noen analytiske brukergrensesnitt. Bør vi virkelig bruke 50% av vårt testbudsjett på å skrive enhetstester for en applikasjon som er integrasjonssentrisk og nesten ikke har noen logikk? Når mangfoldet av applikasjonstyper øker (bots, crypto, Alexa-ferdigheter), er sjansene for å finne scenarier der testpyramiden ikke er den beste kampen.

Det er på tide å berike testporteføljen din og bli kjent med flere testtyper (de neste kulene foreslår få ideer), tankemodeller som testpyramiden, men også matche testtyper til virkelige problemer du står overfor ('Hei, vår API er ødelagt, la oss skrive forbrukerstyrt kontraktstesting! '), diversifisere testene dine som en investor som bygger en portefølje basert på risikoanalyse - vurder hvor problemer kan oppstå og samsvar med noen forebyggingstiltak for å dempe de potensielle risikoene

Et forsiktighetsord: TDD-argumentet i programvareverdenen tar et typisk falsk-dikotomisk ansikt, noen forkynner for å bruke det overalt, andre tror det er djevelen. Alle som snakker i absolutter tar feil:]

Ellers: Du kommer til å savne noen verktøy med fantastisk ROI, noen som Fuzz, lo og mutasjon kan gi verdi på 10 minutter

☺ Gjør det riktig eksempel: Cindy Sridharan foreslår en rik testportefølje i sitt fantastiske innlegg ‘Testing Microservices - the sane way’

Eksempel: YouTube: “Beyond Unit Tests: 5 Shiny Node.JS Test Types (2018)” (Yoni Goldberg)

5 skinnende testteknikker av Yoni Goldberg

️ 13. Komponenttesting kan være din beste sak

Gjør: Hver enhetstest dekker en liten del av applikasjonen, og det er dyrt å dekke hele, mens ende-til-ende-test enkelt dekker mye bakken, men er flakkete og tregere, hvorfor ikke bruke en balansert tilnærming og skrive tester som er større enn enhetstester, men mindre enn tester fra ende til ende? Komponenttesting er den usungne sangen i testverdenen - de gir det beste fra begge verdener: rimelig ytelse og en mulighet til å bruke TDD-mønstre + realistisk og god dekning.

Komponenttester fokuserer på Microservice 'enhet', de jobber mot API, håner ikke noe som hører til Microservice selv (f.eks. Ekte DB, eller i det minste versjonen av minnet til den DB), men stenger alt som er eksternt som samtaler til andre mikroservices. Ved å gjøre det tester vi hva vi distribuerer, nærmer oss appen utover til innover og får stor tillit på en rimelig tid.

Ellers: Du kan bruke lange dager på å skrive enhetstester for å finne ut at du bare har 20% systemdekning

☺ Gjør det riktig eksempel: Supertest gjør det mulig å nærme Express API i prosess (raskt og dekke mange lag)

️ 14. Forsikre deg om at nye utgivelser ikke bryter API-en ved å bruke forbrukerdrevne kontrakter

Gjør: Så Microservice har flere klienter, og du kjører flere versjoner av tjenesten av kompatibilitetshensyn (holder alle fornøyde). Da bytter du litt felt og ‘boom!’, Noen viktige klienter som er avhengige av dette feltet er sinte. Dette er Catch-22 i integrasjonsverdenen: Det er veldig utfordrende for serversiden å vurdere alle forventningene til flere klienter - På den annen side kan ikke klientene utføre noen testing fordi serveren kontrollerer utgivelsesdatoene. Forbrukerdrevne kontrakter og rammeverket PACT ble født for å formalisere denne prosessen med en veldig forstyrrende tilnærming - ikke serveren definerer selve testplanen, heller klienten definerer testene til ... serveren! PACT kan registrere klientens forventning og sette på et delt sted, "megler", slik at serveren kan trekke forventningene og kjøre på alle bygg ved hjelp av PACT-bibliotek for å oppdage ødelagte kontrakter - en klientforventning som ikke blir oppfylt. Ved å gjøre det blir alle API-misforhold mellom server og klient fanget opp tidlig under build / CI og kan spare deg for mye frustrasjon

Ellers: Alternativene er utmattende manuell testing eller frykt for distribusjon

☺ Gjør det riktig eksempel:

️ 15. Test mellomtjenestene dine isolert

Gjør: Mange unngår testing av Middleware fordi de representerer en liten del av systemet og krever en live Express-server. Begge årsakene er gale - Middlewares er små, men påvirker alle eller de fleste forespørsler og kan testes enkelt som rene funksjoner som får {req, res} JS-objekter. For å teste en mellomvarefunksjon, må man bare påkalle den og spionere (ved å bruke Sinon for eksempel) på samspillet med {req, res} -objektene for å sikre at funksjonen utførte riktig handling. Library node-mock-http tar det enda lenger og faktorer {req, res} -objektene sammen med å spionere på oppførselen deres. Den kan for eksempel hevde om http-statusen som ble satt på res-objektet samsvarer med forventningen (se eksempel nedenfor)

Ellers: En feil i Express mellomvare === en feil i alle eller de fleste forespørsler

☺ Gjør det riktig Eksempel: Teste mellomvare isolert uten å utføre nettverkssamtaler og vekke hele Express-maskinen

️ 16. Mål og refaktor ved bruk av statiske analyseverktøy

Gjør: Å bruke statiske analyseverktøy hjelper ved å gi objektive måter å forbedre kodekvaliteten og holde koden vedlikeholdbar. Du kan legge til statiske analyseverktøy i CI-bygget ditt for å avbryte når det finner kodelukt. De viktigste salgsargumentene over vanlig fôring er muligheten til å inspisere kvalitet i sammenheng med flere filer (f.eks. Oppdage duplikasjoner), utføre avansert analyse (f.eks. Kodekompleksitet) og følge historien og fremdriften til kodeproblemer. To eksempler på verktøy du kan bruke er Sonarqube (2600+ stjerner) og kodeklima (1 500+ stjerner)

Kreditt: Keith Holliday

Ellers: Med dårlig kodekvalitet vil feil og ytelse alltid være et problem som ikke et skinnende nytt bibliotek eller moderne funksjoner kan fikse

☺ Gjør det riktig eksempel: CodeClimat, et kommersielt verktøy som kan identifisere komplekse metoder:

️ 17. Kontroller beredskapen din for Node-relatert kaos

Gjør: Merkelig nok, de fleste programvaretesting handler bare om logikk og data, men noe av det verste som skjer (og det er veldig vanskelig å avbøte) er infrastrukturelle problemer. Har du for eksempel testet hva som skjer når prosessminnet ditt er overbelastet, eller når serveren / prosessen dør, eller skjønner overvåkningssystemet ditt når API blir 50% tregere? For å teste og dempe denne typen dårlige ting - Chaos engineering ble født av Netflix. Det tar sikte på å gi bevissthet, rammer og verktøy for å teste appens evne til kaotiske problemer. For eksempel dreper et av dets kjente verktøy, kaosapen, tilfeldig servere for å sikre at tjenesten vår fremdeles kan betjene brukere og ikke stole på en enkelt server (det er også en Kubernetes-versjon, kube-monkey, som dreper pods). Alle disse verktøyene fungerer på verts- / plattformnivå, men hva hvis du ønsker å teste og generere rent Node-kaos som å sjekke hvordan Node-prosessen takler upålitelige feil, ubehandlet løfteavvisning, v8-minne overbelastet med maksimalt tillatt på 1,7 GB eller om UX-en din forblir tilfredsstillende når hendelsesløkken ofte blokkeres? for å adressere dette har jeg skrevet, node-kaos (alfa) som gir alle slags node-relaterte kaotiske handlinger

Ellers: Ingen flukt her, Murphys lov vil treffe produksjonen din uten nåde

☺Doing It Right Eksempel: Node-kaos kan generere alle slags Node.js-pranks, slik at du kan teste hvor spenst din app er til kaos

*** Avsnitt : Måling av testeffektivitet ***

️ 18. Få nok dekning for å være trygg, ~ 80% ser ut til å være lykketallet

Gjør: Hensikten med å teste er å få nok tillit til å bevege deg raskt, tydeligvis jo mer kode som testes, jo mer selvsikker teamet kan være. Dekning er et mål på hvor mange kodelinjer (og grener, utsagn osv.) Som oppnås ved testene. Så hvor mye er nok? 10–30% er åpenbart for lav til å få noen mening om bygnings korrektheten, på den andre siden er 100% veldig dyrt og kan flytte fokuset ditt fra de kritiske banene til de eksotiske hjørnene av koden. Det lange svaret er at det avhenger av mange faktorer som applikasjonstypen - hvis du bygger den neste generasjonen Airbus A380 enn 100% er et must, for et nettsted for tegneseriebilder kan 50% være for mye. Selv om de fleste testentusiaster hevder at riktig dekningsgrense er kontekstuell, nevner de fleste også tallet 80% som en tommel av en regel (Fowler: “i de øvre 80- eller 90-årene”) som antagelig skal tilfredsstille de fleste applikasjoner .

Implementeringstips: Det kan være lurt å konfigurere den kontinuerlige integrasjonen (CI) slik at den har en dekningsterskel (Jest-lenke) og stopper et bygg som ikke overholder denne standarden (det er også mulig å konfigurere terskel per komponent, se kodeeksempel nedenfor) . I tillegg til å vurdere å oppdage reduksjon av byggedekning (når en nylig forpliktet kode har mindre dekning) - dette vil presse utviklere til å heve eller i det minste bevare mengden testet kode. Alt det som er sagt, dekning er bare ett mål, et kvantitativt basert, som ikke er nok til å fortelle om robustheten din tester. Og det kan også lure som illustrert i de neste kulene

Ellers: Tillit og tall går hånd i hånd, uten egentlig å vite at du testet det meste av systemet - det vil også være noe frykt. og frykt vil bremse deg

☺ Eksempel: En typisk dekningsrapport

Istanbul dekningsrapport

☺ Gjør det riktig eksempel: Konfigurere dekning per komponent (ved bruk av Jest)

Kontekstuell dekning

️ 19. Inspiser dekningsrapporter for å oppdage uprøvde områder og andre oddititeter

Gjør: Noen problemer sniker seg rett under radaren og er veldig vanskelig å finne med tradisjonelle verktøy. Dette er egentlig ikke feil, men mer av overraskende applikasjonsatferd som kan ha alvorlig innvirkning. For eksempel blir noen kodeområder aldri eller sjelden påkalt - du trodde at klassen 'PricingCalculator' alltid setter produktprisen, men det viser seg at den faktisk aldri blir påberopt, selv om vi har 10000 produkter i DB og mange salg ... Kodedekning rapporter hjelper deg med å innse om applikasjonen oppfører seg slik du tror den gjør. Annet enn det kan den også fremheve hvilke typer koder som ikke testes - å bli informert om at 80% av koden er testet, forteller ikke om de kritiske delene er dekket. Det er enkelt å generere rapporter - bare kjør appen din i produksjon eller under testing med dekningssporing og se deretter fargerike rapporter som fremhever hvor hyppig hvert kodeområde påberopes. Hvis du tar deg tid til å se på disse dataene - kan du finne noen gotchas

Ellers: Hvis du ikke vet hvilke deler av koden som ikke blir testet, vet du ikke hvor problemene kan komme fra

Antimønstereksempel: Hva er galt med denne dekningsrapporten? basert på et virkelighetsnært scenario der vi sporet applikasjonsbruken vår i QA og fant ut interessante påloggingsmønstre (Hint: mengden påloggingsfeil er ikke-proporsjonal, noe er tydelig galt. Til slutt viste det seg at noen frontendfeil fortsetter å treffe backend-påloggings-API)

️ 20. Mål logisk dekning ved bruk av mutasjonstesting

Gjør: Tradisjonell dekningsmetrikk ligger ofte: Det kan vise deg 100% kodedekning, men ingen av funksjonene dine, selv ikke en, gir riktig svar. Hvorfor det? den måler bare hvilke kodelinjer testen besøkte, men den sjekker ikke om testene faktisk testet noe - hevdet for riktig respons. Som noen som er ute og reiser for forretninger og viser passstemplene sine - dette viser ikke noe som er gjort, bare at han besøkte få flyplasser og hoteller.

Mutasjonsbasert testing er her for å hjelpe ved å måle mengden kode som faktisk ble testet, ikke bare BESØKT. Stryker er et JavaScript-bibliotek for mutasjonstesting, og implementeringen er veldig fin:

(1) den med vilje endrer koden og "planter bugs". For eksempel blir koden newOrder.price === 0 newOrder.price! = 0. Denne "bugs" kalles mutasjoner

(2) det kjører testene, hvis alle lykkes, har vi et problem - testene tjente ikke deres formål med å oppdage feil, mutasjonene er såkalte overlevd. Hvis testene mislyktes, så store, ble mutasjonene drept.

Når du vet at alle eller de fleste av mutasjonene ble drept, gir mye høyere selvtillit enn tradisjonell dekning, og installasjonstiden er lik

Ellers: Du vil bli lurt å tro at 85% dekning betyr at testen din vil oppdage feil i 85% av koden din

Antimønstereksempel: 100% dekning, 0% testing

Gjør det riktig eksempel: Stryker rapporter, et verktøy for mutasjonstesting, oppdager og teller mengden kode som ikke er testet (Mutasjoner)

Stryker-rapport - Å vite at alle eller de fleste av mutasjonene ble drept gir mye høyere selvtillit enn tradisjonell dekning, og oppsettstiden er lik

*** Avsnitt : CI og andre kvalitetstiltak ***

21. Berik dine linter og avbryt bygg som har lofter

Gjør: Linters er en gratis lunsj, med 5 minutters oppsett får du gratis en auto-pilot som vokter koden din og fanger betydelig problem når du skriver. Borte er dagene hvor fôring handlet om kosmetikk (ingen halvkolonier!). I dag kan Linters få alvorlige problemer som feil som ikke kastes riktig og mister informasjon. På toppen av ditt grunnleggende regelverk (som ESLint-standard eller Airbnb-stil), kan du vurdere å inkludere noen spesialiserte Linters som eslint-plugin-chai-forventer som kan oppdage tester uten påstander, eslint-plugin-lover kan oppdage løfter uten løsthet (din kode vil aldri fortsette), eslint-plugin-sikkerhet som kan oppdage ivrige regex-uttrykk som kan bli brukt til DOS-angrep, og eslint-plugin-you-dont-need-lodash-underscore er i stand til å skremme når koden bruker verktøy for biblioteksmetoder som er en del av V8-kjernemetodene som Lodash._map (...)

Ellers: Tenk på en regnfull dag der produksjonen din krasjer, men loggene ikke viser feilstabelsporen. Hva skjedde? Koden din kastet feilaktig et objekt som ikke var feil, og stakelsporen gikk tapt, en god grunn til å slå hodet ditt mot en murvegg. Et oppsett på 5 minutter kan identifisere denne TYPO og redde dagen din

Antimønstereksempel: Det gale feilobjektet blir kastet feil, ingen stack-trace vil vises for denne feilen. Heldigvis fanger ESLint neste produksjonsfeil

Det gale feilobjektet blir kastet feil, ingen stakkespor vil vises for denne feilen. Heldigvis fanger ESLint neste produksjonsfeil

️ 22. Forkorte tilbakemeldingssløyfen med lokal utvikler-CI

Gjør du: Bruke en CI med skinnende kvalitetsinspeksjoner som testing, lofing, sårbarhetssjekk osv.? Hjelp utviklere med å kjøre denne rørledningen også lokalt for å anmode om øyeblikkelig tilbakemelding og forkorte tilbakemeldingssløyfen. Hvorfor? en effektiv testprosess utgjør mange og iterative løkker: (1) test-outs -> (2) feedback -> (3) refactor. Jo raskere tilbakemeldingen er, desto mer forbedrings-iterasjoner kan en utvikler utføre per modul og perfeksjonere resultatene. Når det er snakk om at tilbakemeldingene er sent ute, kan færre forbedrings-iterasjoner pakkes inn i løpet av en enkelt dag, kanskje teamet allerede beveger seg videre til et annet emne / oppgave / modul og er kanskje ikke i stand til å foredle modulen.

Praktisk sett tillater noen CI-leverandører (eksempel: CircleCI load CLI) å lede rørledningen lokalt. Noen kommersielle verktøy som wallaby gir verdifullt og tester innsikt som en prototype for utviklere (ingen tilknytning). Alternativt kan du bare legge til npm-script til package.json som kjører alle kvalitetskommandoer (f.eks. Test, lo, sårbarheter) - bruk verktøy som samtidig for parallellisering og exit-kode uten null hvis ett av verktøyene mislyktes. Nå skal utvikleren bare påberope seg en kommando - f.eks. ‘Npm run quality’ - for å få øyeblikkelig tilbakemelding. Vurder også å avbryte en forpliktelse hvis kvalitetskontrollen mislyktes ved bruk av en githook (husky kan hjelpe)

Ellers: Når kvalitetsresultatene kommer dagen etter koden, blir ikke testing en flytende del av utviklingen, heller en etter den formelle gjenstanden.

☺ Gjør det riktig Eksempel: npm-skript som utfører inspeksjon av kodekvalitet, alle kjøres parallelt på forespørsel eller når en utvikler prøver å skyve ny kode

️ 23. Utfør e2e-testing over et ekte produksjonsspeil

Gjør: Testing fra ende til ende (e2e) er hovedutfordringen for hver CI-rørledning - å skape et identisk flyktig produksjonsspeil på farten med alle relaterte skytjenester kan være kjedelig og dyrt. Å finne det beste kompromisset er spillet ditt: Docker-komposisjon gjør det mulig å lage isolerte dockeriserte omgivelser med identiske containere ved å bruke en enkelt ren tekstfil, men sikkerhetskopieringsteknologien (f.eks. Nettverk, distribusjonsmodell) er forskjellig fra virkelige produksjoner. Du kan kombinere det med ‘AWS Local’ for å jobbe med en stubbe av de virkelige AWS-tjenestene. Hvis du gikk serverløse flere rammer som serverløs og AWS SAM tillater lokal innkalling av Faas-kode.

Det enorme Kubernetes økosystem er ennå ikke i ferd med å formalisere et praktisk praktisk verktøy for lokal og CI-speiling, selv om mange nye verktøy lanseres ofte. Én tilnærming er å kjøre en ‘minimised-Kubernetes’ ved hjelp av verktøy som Minikube og MicroK8s som ligner på den virkelige tingen bare kommer med mindre overhead. En annen tilnærming er å teste over et eksternt ‘ekte-Kubernetes’, noen CI-leverandører (f.eks. Codefresh) har integrert integrasjon med Kubernetes-miljøet og gjør det enkelt å kjøre CI-rørledningen over den virkelige tingen, andre tillater tilpasset skripting mot en ekstern Kubernetes.

Ellers: Å bruke forskjellige teknologier for produksjon og testing krever at det opprettholdes to distribusjonsmodeller og holder utviklerne og ops-teamet atskilt

Eksempel: en CI-rørledning som genererer Kubernetes-klyngen i farten (Kreditt: Dynamiske miljøer Kubernetes)

utplassere:
scene: distribuere
image: registry.gitlab.com/gitlab-examples/kubernetes-deploy
manus:
- ./configureCluster.sh $ KUBE_CA_PEM_FILE $ KUBE_URL $ KUBE_TOKEN
- kubectl opprette ns $ NAMESPACE
- kubectl create secret -n $ NAMESPACE docker-registry gitlab-registry --docker-server = "$ CI_REGISTRY" --docker-username = "$ CI_REGISTRY_USER" --docker-password = "$ CI_REGISTRY_PASSWORD" --docker-email = "$ GITLAB_USER_EMAIL"
- mkdir. generert
- ekko "$ CI_BUILD_REF_NAME- $ CI_BUILD_REF"
- sed -e "s / TAG / $ CI_BUILD_REF_NAME- $ CI_BUILD_REF / g" maler / avtaler.yaml | tee ".generert / deals.yaml"
- kubectl gjelder - navnområde $ NAMESPACE -f .generated / deals.yaml
- kubectl Apply - navneområde $ NAMESPACE -f maler / my-sock-shop.yaml
miljø:
navn: test-for-ci

️ 24. Parallelliser testgjennomføringen

Gjør: Når du gjør det riktig, er testen din 24/7 venn som gir nesten øyeblikkelig tilbakemelding. I praksis kan det ta for lang tid å utføre 500 CPU-avgrenset enhetstest på en enkelt tråd. Heldigvis kan moderne testløpere og CI-plattformer (som Jest, AVA og Mocha-utvidelser) parallellisere testen i flere prosesser og oppnå betydelig forbedring av tilbakemeldingstiden. Noen CI-leverandører parallelliserer også tester på tvers av containere (!) Som forkorter tilbakemeldingssløyfen ytterligere. Enten lokalt over flere prosesser, eller over noen sky CLI ved bruk av flere maskiner - parallelliserende etterspørsel som holder testene autonome fordi hver kan kjøre på forskjellige prosesser

Ellers: Å få testresultater 1 times tid etter å ha dyttet ny kode, siden du allerede koder de neste funksjonene, er en flott oppskrift for å gjøre tester mindre relevante

Gjør det riktig eksempel: Mocha parallel & Jest overskrider lett den tradisjonelle Mokka takket være testing av parallellisering (Kreditt: JavaScript Test-Runners Benchmark)

️ 25. Hold deg unna juridiske problemer ved bruk av lisens og plagieringskontroll

Gjør: Konsesjons- og plagieringproblemer er sannsynligvis ikke din største bekymring akkurat nå, men hvorfor ikke krysse av i denne ruten også om 10 minutter? En rekke npm-pakker som lisenssjekk og plagieringskontroll (kommersiell med gratis plan) kan enkelt bakes i CI-rørledningen din og inspisere for sorger som avhengigheter med restriktive lisenser eller kode som ble kopiert innlimt fra Stackoverflow og tilsynelatende bryter noen opphavsrettigheter

Ellers: Utilsiktet kan utviklere bruke pakker med upassende lisenser eller kopiere og lime inn kommersiell kode og komme inn i juridiske problemer

Gjør det riktig eksempel:

// installere lisenskontroller i ditt CI-miljø eller også lokalt
npm installere -g lisenskontroller
// be den om å skanne alle lisenser og mislykkes med annen exit-kode enn 0 hvis den fant uautorisert lisens. CI-systemet bør fange opp denne feilen og stoppe byggingen
lisenskontroller - sammendrag - feil på BSD

️26. Inspiser konstant for sårbare avhengigheter

Gjør: Selv de mest anerkjente avhengigheter som Express har kjente sårbarheter. Dette kan lett temmes ved å bruke fellesskapsverktøy som npm audit, eller kommersielle verktøy som snyk (tilbyr også en gratis fellesskapsversjon). Begge kan påberopes fra CI-en din på alle bygg

Ellers: Å holde koden ren fra sårbarheter uten dedikerte verktøy vil kreve å kontinuerlig følge online-publikasjoner om nye trusler. Ganske kjedelig

Eksempel: NPM-revisjonsresultater

️ 27. Automatiser avhengighetsoppdateringer

Gjør: Garn og npm siste introduksjon av package-lock.json introduserte en alvorlig utfordring (veien til helvete er brolagt med gode intensjoner) - som standard får ikke pakker lenger oppdateringer. Selv et team som kjører mange nye distribusjoner med ‘npm installering’ og ‘npm-oppdatering’, vil ikke få noen nye oppdateringer. Dette fører i beste fall til subparavhengige pakkeversjoner eller til sårbar kode. Team er nå avhengige av utviklernes velvilje og minne for å oppdatere package.json manuelt eller bruke verktøy som ncu manuelt. En mer pålitelig måte kan være å automatisere prosessen med å få de mest pålitelige versjonsversjonene, selv om det ikke er sølvkule-løsninger, men det er to mulige automatiseringsveier: (1) CI kan mislykkes bygg som har foreldede avhengigheter - ved å bruke verktøy som 'npm utdaterte 'eller' npm-check-updates (ncu) '. Hvis du gjør det, vil tvinge utviklere til å oppdatere avhengigheter. (2) Bruk kommersielle verktøy som skanner koden og automatisk sender trekkforespørsler med oppdaterte avhengigheter. Et interessant spørsmål som gjenstår er hva som bør være avhengighetsoppdateringspolitikken - oppdatering på hver oppdatering genererer for mange overhead, hvis oppdatering rett når en hoved utgave kan peke på en ustabil versjon (mange pakker funnet sårbare de aller første dagene etter at de ble utgitt, se hendelsen om eslint-omfang). En effektiv oppdateringspolicy kan tillate en viss "vestingsperiode" - la koden henge bak @latest i noen tid og versjoner før du anser den lokale kopien som foreldet (f.eks. Lokal versjon er 1.3.1 og depotversjon er 1.3.8)

Ellers: Produksjonen din kjører pakker som eksplisitt er merket av forfatteren deres som risikable

Eksempel: ncu kan brukes manuelt eller i en CI-rørledning for å oppdage i hvilken grad koden henger bak de siste versjonene.

️ 28. Andre, ikke-nodrelaterte CI-tips

Gjør: Dette innlegget er fokusert på testing av råd som er relatert til, eller i det minste kan eksemplifiseres med Node JS. Denne kulen grupperer imidlertid få ikke-nodrelaterte tips som er velkjente

  1. Bruk en deklarativ syntaks. Dette er det eneste alternativet for de fleste leverandører, men eldre versjoner av Jenkins tillater bruk av kode eller UI
  2. Velg en leverandør som har egen Docker-støtte
  3. Mislykkes tidlig, kjør de raskeste testene først. Lag et "Røykprøving" -trinn / milepæl som grupperer flere raske inspeksjoner (f.eks. Lofing, enhetsprøver) og gir snappy tilbakemeldinger til kodeanlegget.
  4. Gjør det enkelt å bla gjennom alle bygge gjenstander inkludert testrapporter, dekningsrapporter, mutasjonsrapporter, logger osv.
  5. Lag flere rørledninger / jobber for hver hendelse, bruk trinn mellom dem. Konfigurer for eksempel en jobb for filialforpliktelser og en annen for master PR. La hver gjenbrukslogikk bruke delte trinn (de fleste leverandører har en mekanisme for gjenbruk av kode
  6. Legg aldri inn hemmeligheter i en stillingserklæring, ta dem fra en hemmelig butikk eller fra jobbkonfigurasjonen
  7. Eksplisitt ujevn versjon i en utgavebygg eller i det minste sikre at utvikleren gjorde det
  8. Bygg bare en gang og utfør alle inspeksjonene over gjenstanden med en enkelt bygning (f.eks. Docker-bilde)
  9. Test i et flyktig miljø som ikke driver tilstand mellom bygg. Cache-nodemoduler er kanskje det eneste unntaket

Ellers: Du vil savne år med visdom

️ 29. Bygg matrise: Kjør de samme CI-trinnene ved å bruke flere Node-versjoner

Gjør: Kvalitetskontroll handler om serendipity, jo mer grunn du dekker, jo heldigere får du til å oppdage problemer tidlig. Når du utvikler gjenbrukbare pakker eller kjører en multi-kundeproduksjon med forskjellige konfigurasjons- og Node-versjoner, må CI kjøre pipeline of tests over alle permutasjoner av konfigurasjoner. Forutsatt at vi for eksempel bruker mySQL for noen kunder og Postgres for andre - noen CI-leverandører støtter en funksjon som heter 'Matrix' som gjør det mulig å kjøre testen mot alle permutasjoner av mySQL, Postgres og multiple Node-versjoner som 8, 9 og 10. Dette gjøres ved å bruke konfigurasjon bare uten ytterligere anstrengelser (forutsatt at du har tester eller andre kvalitetskontroller). Andre CI-er som ikke støtter Matrix, kan ha utvidelser eller justeringer for å tillate det

Ellers: Så etter å ha gjort alt det harde arbeidet med å skrive tester, skal vi bare la bugs snike seg inn på grunn av konfigurasjonsproblemer?

☺ Eksempel: Bruke Travis (CI-leverandør) build-definisjon for å kjøre den samme testen over flere Node-versjoner

språk: node_js
node_js:
  - "7"
  - "6"
  - "5"
  - "4"
installere:
  - npm installasjon
manus:
  - kjøretest på npm

Takk skal du ha. Andre artikler du kanskje vil like

  • Sjekkliste: Node.js beste praksis for produksjon (august 2018)
  • 19 måter å bli en bedre Node.js-utvikler i 2019
  • Node.js sikkerhetspraksis (september 2018)
  • YouTube: 5 avanserte og skinnende testteknikker
  • Node.js beste praksis - 79 beste fremgangsmåter for en robust Node-applikasjon

Vil du ha mer? følg meg på Twitter

Har du ditt eget testtips? PR her, så sørger jeg for å oppdatere denne artikkelen