Внедрите шаблон тестирования Basic AAA уже сегодня!

Все знают, что написание тестов очень важно для разработки программного обеспечения. Это позволяет нам менять коды быстрее и с меньшим количеством ошибок.

Когда дело доходит до внешнего интерфейса, React Testing Library становится популярным, потому что на нем легче тестировать перехватчики React по сравнению с enzyme. Однако способ создания тестов совершенно другой. Итак, в этой статье объясняется, как написать базовые шаблоны тестов AAA с помощью библиотеки тестирования React и TypeScript.

Если вы хотите узнать о других темах библиотеки тестирования React, обратитесь к статьям ниже.

Основной принцип

Самый важный принцип Кента С. Доддса: «Чем больше ваши тесты похожи на то, как используется ваше программное обеспечение, тем больше уверенности они могут вам дать».

Это полностью отличается от того, что сделали инженеры-программисты. Обычно вы пишете тест, чтобы убедиться, что каждая реализация верна. Но React Testing Library утверждает, что ваши тесты должны основываться на том, как пользователь прикасается к ней, а не на вашей реализации.

Давайте посмотрим на простой пример со счетчиком. С ферментом тесты создаются на основе того, как работает реализация.

// __test__/with_enzyme.js
import PlusButton from "/path/to/PlusButton.tsx";
import { shallow } from "enzyme";
it("call handleCountUp", () => {
  const wrapper = shallow(<PlusButton />);
  wrapper.instance().onClick = jest.fn();
  wrapper.update()
  wrapper.instance().handleClick();
  expect(wrapper.instance().onClick).toBeCalled();
});

С другой стороны, в React Testing Library тесты создаются в зависимости от того, как компонент используется.

// __test__/with_testing_library.js
import Counter from "path/to/Counter.tsx";
import { render, fireEvent, waitFor } from "@testing-library/react";
it("count up the number", async () => {
  documentBody = render(<Index />);
  expect(documentBody.getByText("0")).toBeInDocument();
  const plusButton = documentBody.getByText("+");
  fireEvent.click(plusButton);
  
  await waitFor(() => {
    expect(document.getByText("1")).toBeinDocument();
  })
});

Разница очевидна. Enzyme тестирует вашу реализацию, вызывается onClick. Но React Testing Library проверяет пользовательский опыт. Как вы видите, написание тестов библиотеки тестирования React отнимает больше времени, потому что это своего рода интеграционный тест. Так что продолжайте, если вам это нравится!

Установить демонстрационное приложение

Поскольку лучше писать код самому, подготовлено демонстрационное приложение под названием My Pokemon. Обзор описан здесь.

Клонируйте репозиторий демонстрационного приложения и смените ветку.

$ git clone https://github.com/egurinko/react-testing-library-demo.git
$ git switch feature/01_BASIC_GUIDE

Вы можете запустить приложение с помощью

$ yarn
$ yarn start

Запустите тест с

$ yarn test

Подумайте, какие тесты надо писать

Хороший компонент для изучения основ React Testing Library - этоsrc/components/home/Index.tsx.

Хотя вы хотите писать тесты, пожалуйста, прекратите. Перед тем, как писать какой-либо тест, рекомендуется подумать, какой тест вам следует написать. Это позволяет вам обдумать целостную картину компонента и не дает ничего упустить из виду. Если вы обдумываете это, сначала создайте псевдокод.

// src/__test__/integration/home/index.spec.tsx
import * as React from 'react';
import { render} from '@testing-library/react';
import Index from '../../../components/home/Index';
describe('<Index />', () => {
  
  it('shows initial messages', () => {});
  it('shows full texts when read more is clicked', () => {});
});

Написать тест на основе AAA (Arrange, Act, Assert)

Итак, вы выполнили настройку и думали, какие тесты следует писать. Далее будет написано, как вы пишете тест!

Отсюда в этой статье создаются тесты на основе шаблона AAA, потому что он почти стандарт в области программного обеспечения. В этой статье не объясняется сам AAA, потому что он слишком известен и популярен.

Итак, первое, что вам нужно сделать, это Arrange. С помощью библиотеки тестирования React он выполняет рендеринг с необходимыми реквизитами.

// src/__test__/integration/home/index.spec.tsx
import * as React from 'react';
import { render, RenderResult } from '@testing-library/react';
import Index from '../../../components/home/Index';
let documentBody: RenderResult;
describe('<Index />', () => {
  beforeEach(() => {
    // Arrange
    documentBody = render(<Index />);
  });
  it('shows initial messages', () => {});
  it('shows full texts when read more is clicked', () => {});
});

Я рекомендую писать render функцию в beforeEach, потому что почти все тесты требуют рендеринга компонента. Вдобавок к результату render лучше добавить тип с помощью RenderResult. Это позволяет вам использовать вывод типа.

Собственно, для первого теста Act вам не нужно, потому что он проверяет начальное состояние компонента. Итак, напишем Assert часть. Это можно записать так, как показано ниже.

// src/__test__/integration/home/index.spec.tsx
import * as React from 'react';
import { render, RenderResult } from '@testing-library/react';
import Index from '../../../components/home/Index';
let documentBody: RenderResult;
describe('<Index />', () => {
  beforeEach(() => {
    // Arrange
    documentBody = render(<Index />);
  });
  it('shows initial messages', () => {
    // ASSERT
    const welcome = documentBody.getByText('Welcome to Pokemon world!');
    const readMores = documentBody.getAllByText('Read more...');
    expect(welcome).toBeInTheDocument();
    expect(readMores.length).toEqual(2);
  });
  it('shows full texts when read more is clicked', () => {});
});

getByText - один из запросов Библиотеки тестирования DOM. Вы можете выполнять запросы по текстовому контенту, метке, идентификатору и т. Д .toBeInTheDocument является одним из сопоставителей @testing-library/jest-dom. Если хотите узнать больше, пожалуйста, jest-dom.

Чтобы упростить тесты, в приведенном выше примере добавлены два утверждения, но если вы считаете, что этого недостаточно, добавьте еще.

Хорошо, напишем тест для второго. Для второго вам нужно проверить, что новые тексты появляются после нажатия кнопки «Подробнее…». Это Act часть паттерна AAA.

Чтобы щелкнуть DOM, подготовлено fireEvent. В нем много событий DOM. Загляните здесь, чтобы увидеть полную карту событий DOM, которыми fireEvent манипулируют.

import * as React from 'react';
import { render, RenderResult, fireEvent } from '@testing-library/react';
import Index from '../../../components/home/Index';
let documentBody: RenderResult;
describe('<Index />', () => {
  ...
  it('shows full texts', () => {
    const readMores = documentBody.getAllByText('Read more...');
  
    const firstDesc = 'You can check pokemons in Pokedex page';
    expect(
      documentBody.queryByText(
        firstDesc, { exact: false }
      )
    ).not.toBeInTheDocument();
    // ACT and ASSERT
    fireEvent.click(readMores[0]);
    expect(
      documentBody.queryByText(
        firstDesc, { exact: false }
      )
    ).toBeInTheDocument();
    const secondDesc = 'also collectively refers to the 896 fictional';
    expect(
      documentBody.queryByText(
        secondDesc, { exact: false }
      )
    ).not.toBeInTheDocument();
    // ACT and ASSERT
    fireEvent.click(readMores[0]);
    expect(
      documentBody.queryByText(
        secondDesc, { exact: false }
      )
    ).toBeInTheDocument();
  });
});

Вышеупомянутые тесты проверяют, появляются ли новые тексты. Чтобы проверить это, сначала вы должны проверить, не отображается ли целевой текст. Но getByText выдает ошибку, если текста нет. Значит, вам нужно использовать queryByText (Утверждающих элементов нет). Кроме того, используется опция exact: false, чтобы избежать точного совпадения текста. Поскольку текст для меня слишком длинный, он используется здесь. С другой стороны, это увеличивает возможность запроса другого элемента DOM. Так что это зависит от вас.

Запись асинхронного действия

Приведенный выше пример является синхронным. Итак, как вы обрабатываете изменения async DOM, такие как вызов async API, и что-то показываете?

С библиотекой тестирования React это очень просто 👻 Просто используйте waitFor и async await, как показано ниже!

import * as React from 'react';
import { render, RenderResult, fireEvent, waitFor } from '@testing-library/react';
import Index from '../../../components/home/Index';
let documentBody: RenderResult;
describe('<Index />', () => {
  ...
  it('shows full texts', async () => {
    const readMores = documentBody.getAllByText('Read more...');
    // ACT and ASSERT
    const before = documentBody.queryByText('You can');
    expect(before).not.toBeInTheDocument();
    fireEvent.click(readMores[0]);
    await waitFor(()=>{
      const after = documentBody.queryByText('You can');
      expect(after).toBeInTheDocument();
    })
  });
});

Если вы хотите узнать об этом больше, обратитесь, пожалуйста, к этому разделу.

В этой статье основное внимание уделяется основам библиотеки тестирования React с TypeScript. Вы узнали основную концепцию и как написать тестовый шаблон AAA с помощью библиотеки тестирования React.

Поскольку я написал другие статьи о React Testing Library, посмотрите ее, если хотите!

Спасибо!

использованная литература