Skip to main content
Version: 29.7

Testarea de imagine

Testarea de imagine este un instrument foarte util, ori de câte ori doriţi să vă asiguraţi că interfaţa nu se schimbă în mod neaşteptat.

A typical snapshot test case renders a UI component, takes a snapshot, then compares it to a reference snapshot file stored alongside the test. The test will fail if the two snapshots do not match: either the change is unexpected, or the reference snapshot needs to be updated to the new version of the UI component.

Testarea de Imagine cu Jest

O abordare similară se poate lua atunci când vine vorba de testarea componentelor React. În loc de randarea interfeței grafică, care ar necesita construirea întregii aplicații, puteţi utiliza un test de randare pentru a genera rapid o valoare serializabilă pentru arborele React. Consider this example test for a Link component:

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();
});

The first time this test is run, Jest creates a snapshot file that looks like this:

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

Imaginea ar trebui să fie adăugată alături de modificările de cod, şi revizuite ca parte a procesului de code review. Jest uses pretty-format to make snapshots human-readable during code review. On subsequent test runs, Jest will compare the rendered output with the previous snapshot. În cazul în care acestea se potrivesc, testul va trece. If they don't match, either the test runner found a bug in your code (in the <Link> component in this case) that should be fixed, or the implementation has changed and the snapshot needs to be updated.

note

The snapshot is directly scoped to the data you render – in our example the <Link> component with page prop passed to it. This implies that even if any other file has missing props (say, App.js) in the <Link> component, it will still pass the test as the test doesn't know the usage of <Link> component and it's scoped only to the Link.js. Also, rendering the same component with different props in other snapshot tests will not affect the first one, as the tests don't know about each other.

info

Mai multe informaţii despre cum testarea de imagine funcţionează şi de ce am construit-o pot fi găsite pe blog. Va recomandam să citiți acest articol de blog pentru a înțelege mai bine când ar trebui să utilizaţi testarea de imagine. De asemenea, recomandăm vizionarea acestui video egghead despre testarea de imagine cu Jest.

Actualizarea imaginilor

Este evident atunci când un test eșuează după ce s-a introdus un bug. Când se întâmplă, rezolvați problema și asigurați-vă că testele trec din nou. Acum, să vorbim însă despre cazul în care un test eșuează din cauza unei schimbări intenţionate în implementare.

O astfel de situaţie poate apărea în cazul în care schimbăm în mod intenţionat adresa componentei Link din exemplul nostru.

// 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();
});

În acest caz, Jest va afișa următoarele:

Din moment ce am actualizat componenta noastre pentru a indica spre o altă adresă, este firesc să aşteptăm modificări în imaginea acestei componente. Test nostru de imagine eșuează deoarece imagine actualizată pentru componenta noastră nu mai corespunde cu imaginea de referință.

To resolve this, we will need to update our snapshot artifacts. You can run Jest with a flag that will tell it to re-generate snapshots:

jest --updateSnapshot

Acceptați modificările executând comanda de mai sus. Puteţi utiliza, de asemenea, echivalentul -u pentru a regenera imaginile, dacă preferaţi. Aceasta va regenera imaginile de referință pentru toate testele de imagine. Daca am avea si alte teste de imagine eșuate din cauza unor bug-uri neintenţionate, va fi nevoie să rezolvăm bug-urile înainte de a regenera imaginile pentru a evita stocarea imaginilor cu comportament eronat.

Dacă doriţi să limitați testele pentru care se vor regenera imaginile, puteţi specifica o opțiune suplimentară --testNamePattern pentru a re-înregistra imaginile doar pentru acele teste care se potrivesc cu modelul specificat.

You can try out this functionality by cloning the snapshot example, modifying the Link component, and running Jest.

Interactive Snapshot Mode

Failed snapshots can also be updated interactively in watch mode:

Once you enter Interactive Snapshot Mode, Jest will step you through the failed snapshots one test at a time and give you the opportunity to review the failed output.

From here you can choose to update that snapshot or skip to the next:

Once you're finished, Jest will give you a summary before returning back to watch mode:

Inline Snapshots

Inline snapshots behave identically to external snapshots (.snap files), except the snapshot values are written automatically back into the source code. This means you can get the benefits of automatically generated snapshots without having to switch to an external file to make sure the correct value was written.

Example:

First, you write a test, calling .toMatchInlineSnapshot() with no arguments:

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

The next time you run Jest, tree will be evaluated, and a snapshot will be written as an argument to 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>
`);
});

That's all there is to it! You can even update the snapshots with --updateSnapshot or using the u key in --watch mode.

By default, Jest handles the writing of snapshots into your source code. However, if you're using prettier in your project, Jest will detect this and delegate the work to prettier instead (including honoring your configuration).

Property Matchers

Often there are fields in the object you want to snapshot which are generated (like IDs and Dates). If you try to snapshot these objects, they will force the snapshot to fail on every run:

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 these cases, Jest allows providing an asymmetric matcher for any property. These matchers are checked before the snapshot is written or tested, and then saved to the snapshot file instead of the received value:

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",
}
`;

Any given value that is not a matcher will be checked exactly and saved to the snapshot:

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',
}
`;
tip

If the case concerns a string not an object then you need to replace random part of that string on your own before testing the snapshot.
You can use for that e.g. replace() and regular expressions.

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();

Other ways this can be done is using the snapshot serializer or mocking the library responsible for generating the random part of the code you're snapshotting.

Best Practices

Snapshots are a fantastic tool for identifying unexpected interface changes within your application – whether that interface is an API response, UI, logs, or error messages. As with any testing strategy, there are some best-practices you should be aware of, and guidelines you should follow, in order to use them effectively.

1. Treat snapshots as code

Commit snapshots and review them as part of your regular code review process. This means treating snapshots as you would any other type of test or code in your project.

Ensure that your snapshots are readable by keeping them focused, short, and by using tools that enforce these stylistic conventions.

As mentioned previously, Jest uses pretty-format to make snapshots human-readable, but you may find it useful to introduce additional tools, like eslint-plugin-jest with its no-large-snapshots option, or snapshot-diff with its component snapshot comparison feature, to promote committing short, focused assertions.

The goal is to make it easy to review snapshots in pull requests, and fight against the habit of regenerating snapshots when test suites fail instead of examining the root causes of their failure.

2. Tests should be deterministic

Testele voastre trebuie să fie deterministe. Running the same tests multiple times on a component that has not changed should produce the same results every time. Sunteți responsabili să vă asigurați ca imaginile generate nu includ cod specific unor platforme sau alte informatii non-deterministice.

For example, if you have a Clock component that uses Date.now(), the snapshot generated from this component will be different every time the test case is run. In this case we can mock the Date.now() method to return a consistent value every time the test is run:

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

Acum, de fiecare dată când se execută testul, Date.now() va returna 1482363367071 în mod constant. Acest lucru va conduce la aceaşi imagine generată pentru această componentă indiferent când s-ar executa testul.

3. Use descriptive snapshot names

Always strive to use descriptive test and/or snapshot names for snapshots. The best names describe the expected snapshot content. This makes it easier for reviewers to verify the snapshots during review, and for anyone to know whether or not an outdated snapshot is the correct behavior before updating.

For example, compare:

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

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

To:

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

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

Since the latter describes exactly what's expected in the output, it's more clear to see when it's wrong:

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

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

Întrebări frecvente

Are snapshots written automatically on Continuous Integration (CI) systems?

No, as of Jest 20, snapshots in Jest are not automatically written when Jest is run in a CI system without explicitly passing --updateSnapshot. Este de aşteptat ca toate imaginile fac parte din codul care se execută pe CI şi deoarece noi imagini ar trece automat testele, ele nu ar trebui să treacă testele rulate pe un sistem de CI. Este recomandat să se commit-uie întotdeauna toate imaginile pentru a fi menţinute în sistemul de versionare.

Ar trebui commit-uite imaginile serializate?

Da, toate fişierele de imagine ar trebui să fie commit-uite alături de module pe care le acoperă şi testele lor. They should be considered part of a test, similar to the value of any other assertion in Jest. De fapt, imaginile reprezintă starea modulelor sursă la un anumit moment. În acest fel, atunci când modulele sursă sunt modificate, Jest poate spune ce anume s-a schimbat de la versiunea anterioară. Acesta poate oferi și context suplimentar în timpul code review-ului în care recenzenţii pot studia mai bine modificările.

Testarea de imagine funcționeaza numai cu componente React?

Componentele React şi React Native sunt un exemplu foarte bun de utilizare. Cu toate acestea, imaginile pot captura orice valoare serializabilă şi ar trebui utilizate oricând doriți să testați dacă rezultatul este corect. Proiectul Jest conţine multe exemple de testare Jest, rezultatul librăriei de aserțiuni Jest, precum şi loguri din diferite părţi ale codului Jest. See an example of snapshotting CLI output in the Jest repo.

Care este diferenţa între testarea de imagine şi testarea vizuală de regresii?

Testarea de imagine şi testarea vizuală de regresii sunt două moduri distincte de testare a interfețelor UI, şi ele servesc scopuri diferite. Instrumentele de testare vizuală fac capturi de ecran ale paginilor web şi compară imaginile rezultate pixel cu pixel. With Snapshot testing values are serialized, stored within text files, and compared using a diff algorithm. Există diferite compromisuri de luat în considerare şi am enumerat motivele pentru care am construit testarea de imagine pe blogul Jest.

Does snapshot testing replace unit testing?

Testarea de imagine este doar una dintre cele peste 20 asserțiuni, care sunt livrate cu Jest. The aim of snapshot testing is not to replace existing unit tests, but to provide additional value and make testing painless. În unele scenarii, testarea de imagine ar putea elimina nevoia de teste unitare pentru un anumit set de functionalitati (ex. componente React), însă ele pot funcționa la fel de bine împreună.

Care este performanţa testelor de imagine în ceea ce priveşte viteza şi dimensiunea fişierelor generate?

Jest a fost rescris având performanţa în prim plan, şi testarea de imagine nu face nicio excepţie. Deoarece imaginile sunt stocate în fişiere text, modul acesta de testare este rapid şi de încredere. Jest generează câte un fişier pentru fiecare fişier de test, care invocă metoda toMatchSnapshot. The size of the snapshots is pretty small: For reference, the size of all snapshot files in the Jest codebase itself is less than 300 KB.

Cum rezolv conflictele în cadrul fişierelor de imagine?

Fişierele imagine trebuie să reprezinte întotdeauna starea actuală a modulelor pe care le acoperă. Therefore, if you are merging two branches and encounter a conflict in the snapshot files, you can either resolve the conflict manually or update the snapshot file by running Jest and inspecting the result.

Este posibil să se aplice principiile de test-driven development în testarea de imagine?

Deşi este posibil să se scrie manual fişierele de imagine, de obicei nu este o metodă abordabilă. Snapshots help to figure out whether the output of the modules covered by tests is changed, rather than giving guidance to design the code in the first place.

Does code coverage work with snapshot testing?

Yes, as well as with any other test.