Porovnání testovacích knihoven React

Napsal Murat Çatal ✏️

Pokud něco vytvoříte, bez ohledu na to, co to je, měli byste to otestovat, než to naservírujete jiným lidem. Tímto způsobem můžete mít větší důvěru a kontrolu nad svým hotovým produktem, než jej spustíte.

Strategie testování aplikací

Existuje několik způsobů, jak testovat aplikace, vše od malých kousků bloků kódu po obecnější aspekty. Než se ponoříme do rámců a knihoven s nimi spojených, prozkoumáme některé z nejužitečnějších metod hodnocení funkčnosti vaší aplikace.

Test jednotky

Unit test prozkoumá každý malý kousek vašeho kódu. Můžete si to představit jako testování primitivních komponent v jejich životních cyklech. Toto je často nejjednodušší a nejméně nákladná možnost testování.

Integrační test

Máte-li mnoho složených komponent, možná budete chtít vyzkoušet, jak se vzájemně ovlivňují. Můžete to udělat zesměšňováním vašich koncových bodů v rámci integračního testu. To může být nákladnější a složitější než testování jednotek.

End-to-end test

Když přijde čas otestovat celý systém na reálných datech, abyste zjistili, zda vše funguje podle očekávání, nejlepší je úplné testování.

Když začnete psát svůj test, můžete být v pokušení pohrát si s interním podnikáním vaší komponenty a podrobnostmi o implementaci testu, což vás zavede na špatnou cestu. Místo toho byste měli psát testy z pohledu uživatele, abyste vytvořili čistší a přesnější testovací případy. Koneckonců, vaši koncoví uživatelé se nezajímají o interní detaily vaší komponenty, ale zajímá je, co vidí.

Nyní, když jsme stanovili některé obecné osvědčené postupy, pojďme se blíže podívat na některé z nejběžnějších testovacích rámců a běžců. Prozkoumáme křivky učení, schopnosti a klady a zápory spojené s každým.

Jest

Jest je testovací rámec vytvořený a spravovaný Facebookem. Pokud svou aplikaci React sestavíte s create-react-app , můžete začít používat Jest s nulovou konfigurací. Stačí přidat react-test-renderer a @testing-library/react knihovny pro provádění snímků a testování DOM.

S Jest můžete:

  • Proveďte testy snímků, paralelizace a asynchronních metod
  • Vysmívejte se svým funkcím, včetně node_module třetích stran knihovny
  • Provádět nesčetné množství metod tvrzení
  • Zobrazit přehled pokrytí kódem

Teď si ušpiníme ruce nějakým kódem.

Instalace

Předpokládejme, že vaše aplikace je vytvořena pomocí create-react-app .

// For snapshot test
yarn add -D react-test-renderer

// For DOM test
yarn add -D @testing-library/react

Pro existující aplikaci, která není sestavena s create-react-app , postupujte takto:

  1. Přidat závislosti.
yarn add --dev jest babel-jest @babel/preset-env @babel/preset-react react-test-renderer
  1. Nakonfigurujte si svůj babel.
// babel.config.js
module.exports = {
presets: ['@babel/preset-env', '@babel/preset-react'],
};
  1. Přidejte testovací příkaz do package.json .
// package.json
{
"scripts": {
"test": "jest"
}
}

Struktura testování

Nyní, když jste do aplikace přidali testovací soubory, pojďme se ponořit do dalších podrobností o struktuře testování.

Jak je uvedeno níže, create-react-app byl nakonfigurován pro spouštění testů, které mají .spec.js a .test.js soubory.

// MyComponent
export const MyComponent = ({ label }) => {
  return <div>{label}</div>;
};

Máme jednoduchou komponentu, která vezme štítek a zobrazí ho na obrazovce. Dalším krokem je napsat malý test, abyste se ujistili, že se zobrazuje správně.

import React from "react";
import { cleanup, render } from "@testing-library/react";
import { MyComponent } from "./MyComponent";

afterEach(cleanup);

describe("MyCompnent", () => {
  test("should display label", () => {
    const { getByText } = render(<MyComponent label="Test" />);
    expect(getByText("Test")).toBeTruthy();
  });
});

Nyní si projdeme funkce, které chceme otestovat.

afterAll a beforeAll

Spusťte funkce po dokončení testů v aktuálním testovacím souboru nebo před zahájením testu. Pomocí afterAll můžete vyčistit své prostředky a zesměšnit data vytvořená v databázi nebo můžete své konfigurace nastavit v beforeAll .

Tato funkce může vrátit generátor nebo slib a bude čekat, až váš slib nebo funkce generátoru dokončí své provádění, než bude pokračovat.

// MyTestFile.test.js
afterAll(() => {
  cleanResources();
});

beforeAll(() => {
   setupMyConfig();
});

describe("MyComponent",() => {
   test("should do this..",() => {
      expect(prop).toBeTruthy();
   });
});

afterAll spustí se, když všechny vaše testy dokončí své provádění v aktuálním souboru.

afterEach a beforeEach

Na rozdíl od afterAll a beforeAll , jsou tyto funkce volány pro každý testovací případ ve vašem testovacím souboru. Pomocí beforeEach , můžete vytvořit připojení k databázi před spuštěním každého testovacího případu. Jako osvědčený postup byste měli použít afterAll k odstranění vytvořených prvků DOM po každém spuštění testovacího případu.

// MyTestFile.test.js
afterAll(() => {
  resetDomTree();
});

beforeAll(() => {
  createDomElement();
});

describe("MyComponent",() => {
   test("should do this..",() => {
      expect(prop).toBeTruthy();
   });

   test("should do that..",() => {
      expect(prop).toBeTruthy();
   });
});

describe

Tento příkaz umožňuje seskupit související testy a vytvořit čistší výstup.

describe("MyComponent",() => {
   test("should do this..",() => {
      expect(prop).toBeTruthy();
   });

   test("should do that..",() => {
      expect(prop).toBeTruthy();
   });
});

Testování snímku

Test snímku generuje výstup podobný HTML, takže můžete vidět, jak je vaše komponenta strukturována. Je to zvláště užitečné, pokud chcete vidět, jak jsou vaše vlastnosti CSS vkládány podle událostí.

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

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

// generated snapshot
exports[`renders correctly 1`] = `
<a
  className="normal"
  href="http://www.mydomain.com"
  onMouseEnter={[Function]}
  onMouseLeave={[Function]}
>
  My Domain
</a>
`;

Funkce zesměšňování

Zesměšňování během testování je jednou ze základních funkcí, které budete muset implementovat. Jest je skvělý pro zesměšňování nejen vašich funkcí, ale také vašich modulů.

Řekněme například, že chcete otestovat funkci, která načítá uživatele. Používá axios , ale nechceme zasáhnout skutečný koncový bod, protože to není to, co chceme testovat.

import axios from 'axios';
import { Customers } from "./customers";

jest.mock('axios');

test('should fetch users', () => {
  const customers = [{name: 'Bob'}, {name: 'Jenny'}];
  const resp = {data: customers.find(c => c.name = 'Bob')};
  axios.get.mockResolvedValue(resp);

  return Customers.getByFilter("Bob").then(data => expect(data).toEqual({name: 'Bob'}));
});

Jasmín

Stejně jako Jest je Jasmine framework JavaScript a testovací běžec. Než však začnete používat Jasmine, měli byste přidat nějakou konfiguraci.

Na straně profesionálů je zde několik věcí, které můžete s Jasmine dělat:

  • Asynchronní funkční testy
  • Zesměšňování žádostí
  • Vlastní tvrzení kontroly rovnosti
  • Vlastní dotazovací výraz

Pokud jde o nevýhody, níže jsou některé věci, které Jasmine nepodporuje:

  • Snímkové testy
  • Nástroje pro pokrytí kódu
  • Paralelizace (vyžaduje nástroje třetích stran)
  • Manipulace s nativním DOM (vyžaduje nástroj třetí strany, jako je JSDOM)

Jasmine navíc hledá pouze .spec.js soubory; musíte upravit jeho konfiguraci a hledat .test.js soubory také.

Instalace

Jasmine se většinou používá s Enzyme, takže jej budete muset nainstalovat a provést nějaké konfigurace.

yarn add -D babel-cli \
            @babel/register \
            babel-preset-react-app \
            cross-env \
            enzyme \
            enzyme-adapter-react-16 \
            jasmine-enzyme \
            jsdom \
            jasmine

Inicializujte svůj projekt pro Jasmine pomocí následujícího příkazu.

yarn run jasmine init

Nyní vložíme některé konfigurační soubory do složky spec/helper. Budou pro Babel, Enzyme a JSDOM.

// babel.js
require('@babel/register');

// for typescript
require('@babel/register')({
    "extensions": [".js", ".jsx", ".ts", ".tsx"]
});


// enzyme.js or enzyme.ts 
// be sure your file extension is .ts if your project is a typescript project
import jasmineEnzyme from 'jasmine-enzyme';
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

configure({ adapter: new Adapter() });

beforeEach(function() {
  jasmineEnzyme();
});


// jsdom.js

import {JSDOM} from 'jsdom';

const dom = new JSDOM('<html><body></body></html>');
global.document = dom.window.document;
global.window = dom.window;
global.navigator = dom.window.navigator;

Nakonec upravte konfigurační soubor Jasmine, abyste zajistili správné načtení konfigurace Babel, Enzyme a JSDOM.

Nyní je čas přejít na spec/suppor/jasmine.json .

// the important part here is we should load babel firstly.

// for normal projects
"helpers": [
  "helpers/babel.js",
  "helpers/**/*.js"
],

// for typescript projects
"helpers": [
  "helpers/babel.js",
  "helpers/**/*.{js,ts}"
],

Pojďme se podívat, jak píšeme test s Jasmine. Dotkneme se také Enzymu.

Většina pomocných funkcí, jako je afterAll , beforeAll , afterEach a beforeEach , jsou podobné Jest, takže se pojďme ponořit do toho, jak napsat základní test pro komponentu React, abychom viděli její strukturu.

const Utils = React.addons.TestUtils;
let element;
beforeEach(() => {
  element = React.createElement(
      MyComponent,
      {
        label: 'Hello'
      });
});

afterEach(() => {
  element = null;
});

describe('MyComponent', function() {
  it('can render without error', function() {
    const component = Utils.renderIntoDocument(element);
    expect(component).not.toThrow();
  });
})

Vlastní párovač

V Jasmine můžete napsat vlastní funkce dohazovače, které se globálně znovu použijí v každé testovací specifikaci. Vlastní přiřazovací nástroj by se mohl hodit, pokud například máte specifickou skupinu testovacích přiřazování, které se často používají.

Vlastní porovnávače by měly vrátit objekt, který má pass a message vlastnosti. A pass vlastnost zkontroluje, zda jsou podmínky v platném stavu. message je pole, které se zobrazuje ve stavu selhání.

const customMatchers = {
  toBeValidAgeRange: function() {
    return {
      compare: function(actual, expected) {
         var result = {};
         result.pass = (actual > 18 && actual <=35);
         result.message = actual + ' is not valid';   
         return result;
      }
    };
  }
};


describe("Custom matcher", function() {
  beforeEach(function() {
    // register our custom matcher
    jasmine.addMatchers(customMatchers);
  });
  it("should be valid age", function() {
    expect(19).toBeValidAgeRange();
  });

  it("should fail", function() {
    expect(38).toBeValidAgeRange();
  });
});

Vlastní kontrola rovnosti

Někdy může být nutné porovnat dva objekty nebo změnit chování kontroly rovnosti pro porovnání primitivních typů. Jasmine má dobré API pro kontrolu rovnosti.

Funkce vlastní kontroly musí mít dva parametry:první pochází z expect a druhý pochází z assertion funkce. Také musí vrátit boolean nebo undefined . Pokud vrátí undefined , to znamená, že funkce rovnosti není pro tyto parametry vhodná.

function myObjectChecker(first, second) {
  if (typeof first === 'object' && typeof second === 'object' && 
      first.hasOwnProperty('name') && second.hasOwnProperty('name')) {
    return first.name === second.name;
  }
}

beforeEach(() => {
  jasmine.addCustomEqualityTester(myObjectChecker);
});

describe('MyComponent', function() {
  it('can render without error', function() {
    expect({name: 'John'}).toEqual({name:'John'});
  });
});

react-testing-library

Tato knihovna, kterou vytvořil Kent C. Dodds a spravuje ji obrovská komunita vývojářů, vám umožňuje testovat komponenty, aniž byste se dotkli jejich interního podnikání – což vám zase umožňuje provádět výkonnější testovací případy a přitom udržovat uživatelský dojem na prvním místě.

S react-testing-library , můžete:

  • Dotaz na prvky v textu, label , displayValue , role a testId
  • Spustit jakoukoli událost
  • Počkejte, až se prvek zobrazí s wait

Nemůžete však:

  • Proveďte mělké vykreslování
  • Přístup k interním záležitostem vašich komponent, jako jsou stavy

Instalace

yarn add -D @testing-library/react

Nyní k té zábavnější části…

import React from 'react';
import { render, RenderOptions, RenderResult } from '@testing-library/react';

describe('MyComponent', () =&gt; {
  test('should label be in document', () =&gt; {
    const {container, util} = render(&lt;MyComponent label='Hello' /&gt;);
    const label = utils.getByText('Hello');
    expect(label).toBeInTheDocument();
  });
}

( Dokumentace rozhraní API )

Enzym

Enzyme je nástroj pro testování JavaScriptu, který má vývojářům pomoci snadno testovat komponenty React. Je spravován Airbnb a patří mezi nejpoužívanější frameworky.

Enzym vám umožňuje:

  • Používejte mělké vykreslování
  • Přístup k podnikovým implementacím vašich komponent
  • Proveďte úplné vykreslování modelu DOM
  • Použijte react-hooks v mělkém vykreslování, s určitými omezeními

Instalace

yarn add -D enzyme enzyme-adapter-react-16

Vytvořte enzyme.js v src složky, jak je znázorněno níže.

import Enzyme, { configure, shallow, mount, render } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

configure({ adapter: new Adapter() });
export { shallow, mount, render };
export default Enzyme;

Nyní pojďme udělat nějaké kódování.

Mělké vykreslování

import React from 'react';
// we are importing from our enzyme.js
import { shallow } from './enzyme';

import MyComponent from './MyComponent';

describe('MyComponent', () =&gt; {
  test('renders correct text in item', () =&gt; {
    const wrapper = shallow(&lt;MyComponent label="Hello" /&gt;);

    //Expect the child of the first item to be an array
    expect(wrapper.find('.my-label').get(0).props.children).toEqual('Hello');
  });
});

Úplné vykreslování DOM

describe('&lt;Foo /&gt;', () =&gt; {
  it('calls componentDidMount', () =&gt; {
    sinon.spy(Foo.prototype, 'componentDidMount');
    const wrapper = mount(&lt;Foo /&gt;);
    expect(Foo.prototype.componentDidMount).to.have.property('callCount', 1);
  });
}

Dejte si pozor na componentDidMount ! Přistoupili jsme k internímu podnikání naší komponenty, což vás může vést k napsání nesprávných testovacích případů, pokud si nedáte pozor.

End-to-end testy

Až do této chvíle jsme zkoumali testovací knihovny z hlediska testů zápisové jednotky nebo integračních testů. Před spuštěním výroby však můžeme také potřebovat plně integrovaný test s backendem. Za tímto účelem se podíváme na dvě knihovny:Cypress a Puppeteer.

Cypřiš

Cypress vám umožňuje psát testy bez dalšího testovacího rámce. Má pěkné API pro interakci s komponentami stránky a podporuje Chrome/Chromium, Canary a Electron.

Co můžete dělat;

  • Cestování časem
  • Snímky obrazovky a videa
  • Automatické čekání
  • Ovládejte síťový provoz, aniž byste se dotkli svého serveru a otestovali tak okrajové případy
  • Paralelizace

K instalaci a spuštění Cypress použijte následující řádky kódu.

yarn add -D cypress
yarn run cypress open

Nyní napíšeme nějaké testy.

Nejprve vytvořte soubor s názvem my-test_spec.js .

describe('My First Test', function() {
  it('Gets, types and asserts', function() {
    cy.visit('https://www.mydomain.com')
    cy.contains('login').click()

    cy.url().should('include', '/login')

    cy.get('.email')
      .type('[email protected]')
      .should('have.value', '[email protected]')
  })
})

Loutkář

Puppeteer není testovací rámec JavaScriptu – je to bezhlavá knihovna Chromium. Můžete spustit svůj prohlížeč Chromium a pomocí poskytnutého rozhraní API procházet mezi stránkami, získávat tlačítka a klikat na ně.

Puppeteer běží na skutečném prohlížeči a umožňuje vám psát vaše end-to-end testy s API podobným prohlížeči.

Chcete-li nainstalovat, zadejte následující řádek kódu.

yarn add -D jest-puppeteer puppeteer jest

Poté do package.json zadejte následující .

// package.json
{
 jest: {
    "preset": "jest-puppeteer"
  }
}

Níže je uveden kód pro naše testování e2e.

beforeAll(async ()=&gt; {
  await page.goTo('http://mydomain.com');
});

describe('Visit MyDomain', () =&gt; {
  test('should have login text', () =&gt; {
     await expect(page).toMatch('login');
  });
});

Porovnávání testovacích knihoven a rámců React přímo u sebe

Až dosud jsme se zabývali funkcemi knihoven a jejich implementací do našich projektů. Nyní se podívejme na některá srovnávací kritéria a porovnejme výsledky mezi knihovnami.

Jest versus Jasmine

Jak jsme zmínili na začátku, Jest a Jasmine se používají jako testovací frameworky. Své testovací případy seskupujete do popisných bloků a své testy zapisujete do test nebo it funkce.

Nyní si naše srovnání rozebereme do praktické a snadno čitelné tabulky.

Tady je to, co se mi na Jestovi líbí nejvíc:

  • Je vyžadována nulová konfigurace
  • Podpora testování snímků
  • Podpora pokrytí kódem
  • Funkce zesměšňování

Co se týče Jasmine, nejužitečnější funkcí je její výsměch. I když je to poněkud omezené, pro většinu případů použití to stačí.

V současné době používám Jest v produktu kvůli jeho nativní podpoře v rámci komunity React a protože slouží našim potřebám z hlediska testování komponent React více než Jasmine.

react-testing-library versus Enzym

Mezi nejdůležitější aspekty při psaní testů pro komponentu patří vaše util funkcí. Mohou vás nutit psát čistší a pravdivější způsob testování nebo vést k nesprávnému psaní testů, pokud jde o exportovaná rozhraní API.

Při psaní testů pro vaše komponenty se příliš nezabývejte detaily implementace. Pamatujte, zkuste o tom přemýšlet z pohledu uživatele. Pomůže vám to vytvářet lepší sady testů, které vám pomohou cítit se jistěji ve vašich testech.

Pro většinu případů použití dávám přednost react-testing-library , především proto, že jeho exportovaná API neumožňují používat interní API komponenty, což vás nutí psát lepší testy. Navíc není vyžadována žádná konfigurace.

Enzyme vám na druhou stranu umožňuje používat interní API komponenty, které může zahrnovat metody životního cyklu nebo stav.

Použil jsem jak enzym, tak react-testing-libraries v mnoha projektech. Často jsem však zjistil, že react-testing-library dělá věci jednodušší.

Cypřiš versus loutkář

Testování vašich kritických stránek od začátku do konce vám může zachránit život před přechodem do produkce. Níže je souhrnné srovnání Cypress a Puppeteer.

Vzhledem k tomu, že Cypress je testovací rámec, má oproti Puppeteer mnoho výhod, když věci, které chcete vyvinout, musí být rychlé. Jeho API jsou přátelská pro vývojáře a umožňují vám napsat test, jako byste psali unit test. Puppeteer není testovací framework, ale prohlížeč. Jeho API nejsou přívětivá pro vývojáře, ale jsou výkonná, protože máte přístup k API prohlížeče. Proto přichází se strmější křivkou učení než Cypress.

Závěr

Jak vidíte, každá testovací metoda, knihovna a framework přináší své výhody a nevýhody v závislosti na případu použití a typech dat, která chcete analyzovat. Po vyhodnocení každého testovacího rámce s ohledem na tyto faktory je jasné, že react-testing-library je nejhodnotnější a nejlogičtější volbou pro jednotkové a integrační testy. Pro testování end-to-end je Cypress vhodnou volbou pro své snadno naučitelné API.

Plná viditelnost do produkčních aplikací React

Ladění aplikací React může být obtížné, zvláště když uživatelé zaznamenají problémy, které je obtížné reprodukovat. Pokud vás zajímá monitorování a sledování stavu Redux, automatické zobrazování chyb JavaScriptu a sledování pomalých síťových požadavků a doby načítání komponent, vyzkoušejte LogRocket.

LogRocket je jako DVR pro webové aplikace, zaznamenává doslova vše, co se děje ve vaší aplikaci React. Namísto hádání, proč k problémům dochází, můžete agregovat a hlásit, v jakém stavu byla vaše aplikace, když k problému došlo. LogRocket také monitoruje výkon vaší aplikace a vytváří zprávy s metrikami, jako je zatížení procesoru klienta, využití paměti klienta a další.

Middlewarový balíček LogRocket Redux přidává do vašich uživatelských relací další vrstvu viditelnosti. LogRocket zaznamenává všechny akce a stav z vašich obchodů Redux.

Modernizujte způsob ladění aplikací React – začněte sledovat zdarma.

Příspěvek Porovnání testovacích knihoven React appeared first on LogRocket Blog.