Hopp til hovedinnhold
Versjon: 30.0

Testing med øyeblikksbilder

Unofficial Beta Translation

This page was AI-translated by PageTurner (beta). Not officially endorsed by the project. Found an error? Report issue →

Tester med øyeblikksbilder er et svært nyttig verktøy når du vil forsikre deg om at brukergrensesnittet ditt ikke endrer seg uventet.

En typisk test for øyeblikksbilder gjengir en UI-komponent, tar et øyeblikksbilde, og sammenligner det med en referanse-øyeblikksbildfil lagret sammen med testen. Testen vil feile hvis de to øyeblikksbildene ikke samsvarer: enten er endringen uventet, eller referanse-øyeblikksbildet må oppdateres til den nye versjonen av UI-komponenten.

Testing med øyeblikksbilder i Jest

En lignende tilnærming kan brukes ved testing av React-komponenter. I stedet for å gjengi det grafiske grensesnittet, som ville krevd å bygge hele applikasjonen, kan du bruke en testgjengiver for raskt å generere en serialiserbar verdi for React-treet ditt. Se dette testeksemplet for en Link-komponent:

import renderer from 'react-test-renderer';
import Link from '../Link';

it('renders correctly', () => {
const tree = renderer
.create(<Link page="http://www.facebook.com">Facebook</Link>)
.toJSON();
expect(tree).toMatchSnapshot();
});

Første gang denne testen kjøres, oppretter Jest en øyeblikksbildfil som ser slik ut:

exports[`renders correctly 1`] = `
<a
className="normal"
href="http://www.facebook.com"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
Facebook
</a>
`;

Øyeblikksbildet bør lagres i versjonskontroll sammen med kodeendringer og gjennomgås som del av kodegjennomgangen din. Jest bruker pretty-format for å gjøre øyeblikksbilder menneskelig lesbare under kodegjennomgang. Ved senere testkjøringer vil Jest sammenligne det gjengitte resultatet med forrige øyeblikksbilde. Hvis de samsvarer, vil testen passere. Hvis ikke, har enten testkjøringen funnet en feil i koden din (i <Link>-komponenten i dette tilfellet) som bør fikses, eller implementasjonen er endret og øyeblikksbildet må oppdateres.

notat

Øyeblikksbildet er begrenset til dataene du gjengir – i vårt eksempel <Link>-komponenten med page-prop som sendes til den. Dette betyr at selv om andre filer har manglende props (f.eks. App.js) i <Link>-komponenten, vil testen fortsatt passere fordi testen ikke kjenner til bruken av <Link>-komponenten og den kun gjelder Link.js. Å gjengi samme komponent med ulike props i andre øyeblikksbildetester vil heller ikke påvirke den første, siden testene ikke kjenner til hverandre.

info

Mer informasjon om hvordan testing med øyeblikksbilder fungerer og hvorfor vi bygde det finner du i utgivelsesblogginnlegget. Vi anbefaler å lese dette blogginnlegget for å få god forståelse av når du bør bruke testing med øyeblikksbilder. Vi anbefaler også å se denne egghead-videoen om Testing med øyeblikksbilder i Jest.

Oppdatere øyeblikksbilder

Det er enkelt å oppdage når en øyeblikksbildetest feiler etter at en feil er introdusert. Når dette skjer, løser du problemet og sikrer at testene dine passerer igjen. La oss nå snakke om når en øyeblikksbildetest feiler på grunn av en bevisst implementeringsendring.

En slik situasjon kan oppstå hvis vi bevisst endrer adressen som Link-komponenten i eksemplet vårt peker til.

// Updated test case with a Link to a different address
it('renders correctly', () => {
const tree = renderer
.create(<Link page="http://www.instagram.com">Instagram</Link>)
.toJSON();
expect(tree).toMatchSnapshot();
});

I så fall vil Jest vise dette resultatet:

Siden vi nettopp oppdaterte komponenten til å peke til en annen adresse, er det forventelig at øyeblikksbildet for denne komponenten endres. Testen vår feiler fordi det oppdaterte komponentens øyeblikksbilde ikke lenger samsvarer med referanse-øyeblikksbildet for denne testen.

For å løse dette må vi oppdatere referanse-øyeblikksbildene våre. Du kan kjøre Jest med en flagg som instruerer den om å regenerere øyeblikksbilder:

jest --updateSnapshot

Kjør kommandoen ovenfor for å godta endringene. Du kan også bruke den tilsvarende enkeltbokstavsflagen -u for å generere snapshots på nytt. Dette vil generere snapshot-artefakter på nytt for alle feilede snapshot-tester. Hvis vi hadde flere feilede snapshot-tester på grunn av utilsiktede feil, måtte vi fikset feilen før regenerering for å unngå å lagre snapshots av feilatferd.

Hvis du vil begrense hvilke snapshot-testtilfeller som skal regenereres, kan du legge til --testNamePattern-flagen for kun å ta nye snapshots for tester som matcher mønsteret.

Du kan teste denne funksjonaliteten ved å klone snapshot-eksempelet, endre Link-komponenten og kjøre Jest.

Interaktiv Snapshot-modus

Feilede snapshots kan også oppdateres interaktivt i watch-modus:

Når du går inn i Interaktiv Snapshot-modus, vil Jest gå deg gjennom feilede snapshots én test om gangen og gi deg mulighet til å se gjennom feilutdataene.

Herfra kan du velge å oppdatere snapshotet eller hoppe til neste:

Når du er ferdig, gir Jest deg en oppsummering før du går tilbake til watch-modus:

Inline Snapshots

Inline Snapshots fungerer identisk med eksterne snapshots (.snap-filer), men snapshot-verdiene skrives automatisk tilbake i kildekoden. Dette gir deg fordelene med automatisk genererte snapshots uten å måtte bytte til eksterne filer for å verifisere korrekte verdier.

Eksempel:

Først skriver du en test som kaller .toMatchInlineSnapshot() uten argumenter:

it('renders correctly', () => {
const tree = renderer
.create(<Link page="https://example.com">Example Site</Link>)
.toJSON();
expect(tree).toMatchInlineSnapshot();
});

Neste gang du kjører Jest, vil tree bli evaluert og et snapshot skrives som argument til toMatchInlineSnapshot:

it('renders correctly', () => {
const tree = renderer
.create(<Link page="https://example.com">Example Site</Link>)
.toJSON();
expect(tree).toMatchInlineSnapshot(`
<a
className="normal"
href="https://example.com"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
Example Site
</a>
`);
});

Det er alt! Du kan også oppdatere snapshots med --updateSnapshot eller ved å bruke u-tasten i --watch-modus.

Som standard håndterer Jest skriving av snapshots til kildekoden. Hvis du bruker prettier i prosjektet, vil Jest imidlertid oppdage dette og delegere arbeidet til prettier (inkludert å følge din konfigurasjon).

Property Matchers

Ofte inneholder objekter du vil ta snapshot av genererte felt (som ID-er og datoer). Hvis du tar snapshot av disse, vil de føre til snapshot-feil ved hver kjøring:

it('will fail every time', () => {
const user = {
createdAt: new Date(),
id: Math.floor(Math.random() * 20),
name: 'LeBron James',
};

expect(user).toMatchSnapshot();
});

// Snapshot
exports[`will fail every time 1`] = `
{
"createdAt": 2018-05-19T23:36:09.816Z,
"id": 3,
"name": "LeBron James",
}
`;

For slike tilfeller lar Jest deg bruke asymmetriske matchere for egenskaper. Disse matcherene kontrolleres før snapshotet skrives eller testes, og lagres i snapshot-filen i stedet for mottatte verdier:

it('will check the matchers and pass', () => {
const user = {
createdAt: new Date(),
id: Math.floor(Math.random() * 20),
name: 'LeBron James',
};

expect(user).toMatchSnapshot({
createdAt: expect.any(Date),
id: expect.any(Number),
});
});

// Snapshot
exports[`will check the matchers and pass 1`] = `
{
"createdAt": Any<Date>,
"id": Any<Number>,
"name": "LeBron James",
}
`;

Verdier som ikke er matchere vil bli kontrollert nøyaktig og lagret i snapshotet:

it('will check the values and pass', () => {
const user = {
createdAt: new Date(),
name: 'Bond... James Bond',
};

expect(user).toMatchSnapshot({
createdAt: expect.any(Date),
name: 'Bond... James Bond',
});
});

// Snapshot
exports[`will check the values and pass 1`] = `
{
"createdAt": Any<Date>,
"name": 'Bond... James Bond',
}
`;
tips

Hvis det gjelder en streng og ikke et objekt, må du erstatte den tilfeldige delen av strengen manuelt før snapshot-testing.
Du kan bruke f.eks. replace() med regulære uttrykk.

const randomNumber = Math.round(Math.random() * 100);
const stringWithRandomData = `<div id="${randomNumber}">Lorem ipsum</div>`;
const stringWithConstantData = stringWithRandomData.replace(/id="\d+"/, 123);
expect(stringWithConstantData).toMatchSnapshot();

Alternative løsninger inkluderer snapshot-serializere eller mocking av biblioteket som genererer den tilfeldige delen av koden.

Beste praksis

Snapshots er et fantastisk verktøy for å identifisere uventede endringer i grensesnittet i applikasjonen din – enten det er et API-svar, brukergrensesnitt, logger eller feilmeldinger. Som med enhver teststrategi finnes det noen beste praksiser du bør være oppmerksom på, og retningslinjer du bør følge for å bruke dem effektivt.

1. Behandle snapshots som kode

Committ snapshots og gjennomgå dem som en del av din vanlige kodegjennomgangsprosess. Dette innebærer å behandle snapshots på samme måte som enhver annen type test eller kode i prosjektet ditt.

Sikre at snapshotsene dine er lesbare ved å holde dem fokuserte, korte, og ved å bruke verktøy som håndhever disse stilistiske konvensjonene.

Som tidligere nevnt bruker Jest pretty-format for å gjøre snapshots menneskelesbare, men du kan finne det nyttig å introdusere tilleggsverktøy som eslint-plugin-jest med dens no-large-snapshots-alternativ, eller snapshot-diff med sin komponent-snapshot-sammenligningsfunksjon, for å fremme commiting av korte, fokuserte påstander.

Målet er å gjøre det enkelt å gjennomgå snapshots i pull requests, og bekjempe vanen med å regenerere snapshots når testsuiten feiler i stedet for å undersøke de underliggende årsakene til feilen.

2. Tester bør være deterministiske

Testene dine bør være deterministiske. Å kjøre de samme testene flere ganger på en komponent som ikke har endret seg, bør produsere de samme resultatene hver gang. Du er ansvarlig for å sikre at genererte snapshots ikke inkluderer plattformspesifikke eller andre ikke-deterministiske data.

For eksempel, hvis du har en Clock-komponent som bruker Date.now(), vil snapshotet generert fra denne komponenten være forskjellig hver gang testen kjøres. I dette tilfellet kan vi mocke Date.now()-metoden for å returnere en konsistent verdi hver gang testen kjøres:

Date.now = jest.fn(() => 1_482_363_367_071);

Nå vil Date.now() returnere 1482363367071 konsekvent hver gang snapshot-testen kjøres. Dette vil resultere i at samme snapshot genereres for denne komponenten uavhengig av når testen kjøres.

3. Bruk beskrivende snapshot-navn

Streif alltid etter å bruke beskrivende test- og/eller snapshot-navn for snapshots. De beste navnene beskriver det forventede snapshot-innholdet. Dette gjør det enklere for gjennomførere å verifisere snapshotsene under gjennomgang, og for hvem som helst å vite om et utdatert snapshot representerer korrekt atferd før det oppdateres.

For eksempel, sammenlign:

exports[`<UserName /> should handle some test case`] = `null`;

exports[`<UserName /> should handle some other test case`] = `
<div>
Alan Turing
</div>
`;

Med:

exports[`<UserName /> should render null`] = `null`;

exports[`<UserName /> should render Alan Turing`] = `
<div>
Alan Turing
</div>
`;

Siden sistnevnte beskriver nøyaktig hva som forventes i output, er det tydeligere å se når det er feil:

exports[`<UserName /> should render null`] = `
<div>
Alan Turing
</div>
`;

exports[`<UserName /> should render Alan Turing`] = `null`;

Ofte stilte spørsmål

Skrives snapshots automatisk på Continuous Integration (CI)-systemer?

Nei, fra Jest 20 skrives ikke snapshots automatisk når Jest kjøres i et CI-system uten eksplisitt å sende med --updateSnapshot. Det forventes at alle snapshots er en del av koden som kjøres på CI, og siden nye snapshots automatisk passerer, bør de ikke få en testkjøring til å passere på et CI-system. Det anbefales å alltid committe alle snapshots og å holde dem under versjonskontroll.

Bør snapshot-filer committes?

Ja, alle snapshot-filer bør committes sammen med modulene de dekker og deres tester. De bør betraktes som en del av en test, på samme måte som verdien av enhver annen påstand i Jest. Snapshots representerer faktisk tilstanden til kildemodulene til enhver tid. På denne måten kan Jest, når kildemodulene endres, se hva som har endret seg fra forrige versjon. Det kan også gi mye ekstra kontekst under kodegjennomgang der gjennomførere kan studere endringene dine bedre.

Fungerer snapshot-testing kun med React-komponenter?

React- og React Native-komponenter er gode bruksområder for testing med øyeblikksbilder. Øyeblikksbilder kan imidlertid fange opp enhver serialiserbar verdi og bør brukes når målet er å teste om utdataene er korrekte. Jest-repositoriet inneholder mange eksempler på testing av Jest sin egen output, output fra Jest sitt påstandsbibliotek samt loggmeldinger fra ulike deler av Jest-kodebasen. Se et eksempel på øyeblikksbilder av CLI-output i Jest-repositoriet.

Hva er forskjellen mellom snapshot-testing og visuell regresjonstesting?

Snapshot-testing og visuell regresjonstesting er to distinkte måter å teste brukergrensesnitt på som tjener ulike formål. Verktøy for visuell regresjonstesting tar skjermbilder av nettsider og sammenligner resulterende bilder piksel for piksel. Ved snapshot-testing serialiseres verdier, lagres i tekstfiler og sammenlignes med en diff-algoritme. Det er ulike avveininger å vurdere, og vi har listet opp grunnene til at snapshot-testing ble utviklet i Jest-bloggen.

Erstatter snapshot-testing enhetstesting?

Snapshot-testing er bare én av over 20 assertions som følger med Jest. Målet er ikke å erstatte eksisterende enhetstester, men å gi ekstra verdi og gjøre testing problemfri. I noen scenarier kan snapshot-testing potensielt fjerne behovet for enhetstesting for spesifikke funksjonaliteter (f.eks. React-komponenter), men de kan også fungere sammen.

Hva er ytelsen til snapshot-testing når det gjelder hastighet og størrelse på genererte filer?

Jest er omskrevet med ytelse i tankene, og snapshot-testing er intet unntak. Siden snapshots lagres i tekstfiler, er denne testmetoden rask og pålitelig. Jest genererer en ny fil for hver testfil som bruker toMatchSnapshot-matcheren. Snapshots-filenes størrelse er relativt liten: Størrelsen på alle snapshot-filer i Jest-kodebasen er under 300 KB.

Hvordan løser jeg konflikter i snapshot-filer?

Snapshot-filer må alltid gjenspeile gjeldende tilstand for modulene de dekker. Hvis du fletter sammen to grener og oppdager konflikt i snapshot-filer, kan du enten løse konflikten manuelt eller oppdatere snapshot-filen ved å kjøre Jest og inspisere resultatet.

Kan man bruke testdrevet utvikling med snapshot-testing?

Selv om det er mulig å skrive snapshot-filer manuelt, er dette vanligvis ikke hensiktsmessig. Snapshots hjelper til med å identifisere endringer i output fra testdekte moduler, snarere enn å gi veiledning for kodedesign fra starten.

Fungerer kodedekning med snapshot-testing?

Ja, akkurat som med alle andre tester.