Ошибки и недочеты - это реальность. Их невозможно избежать. Единственное, что мы можем сделать, - это минимизировать их вероятность:

  1. Выявление ошибок и их устранение с помощью автоматизированного тестирования (тема этого сообщения в блоге).
  2. Повторное использование компонентов, которые зарекомендовали себя как безошибочные как в реальных проектах, так и при автоматизированном тестировании. Это можно сделать с помощью инструмента и платформы Bit (Github). Bit позволяет быстро и легко обмениваться, повторно использовать и управлять повторно используемыми компонентами. Он даже запускает модульные тесты перед отправкой компонентов в компонентный центр Bit.
    Я не буду больше обсуждать это в этом сообщении в блоге, но вы можете узнать больше об этом здесь:


Одним из аспектов тестирования является тестирование пользовательского интерфейса.

Тестирование пользовательского интерфейса - это метод, используемый для тестирования графического пользовательского интерфейса. Он включает в себя проверку экранов, различных элементов управления и т. Д., Чтобы убедиться, что они работают должным образом в различных условиях.

В этом посте мы рассмотрим две библиотеки, которые позволяют нам проводить тестирование пользовательского интерфейса в нашем приложении React:

Реагировать-Тестирование-Библиотека



React-Testing-Library - это библиотека для DOM-тестирования, разработанная компанией Kent. C Доддс . Он заменяет Enzyme и предоставляет легкие служебные функции для использования с react-dom.

Он больше ориентирован на тестирование пользовательского интерфейса приложений React. Это означает, что он не только тестирует экземпляры компонентов React, но также может тестировать поведение элементов, отображаемых компонентами React. Так что это отличная библиотека для тестирования пользовательского интерфейса React.

Библиотека тестирования React заставляет следовать принципу:

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

Тестирование снимков

Тестирование моментальных снимков - это функция в функции тестирования реакции, в которой мы можем получить моментальный снимок компонента React и протестировать их. Это важно, потому что мы добавим или обновим компонент и, возможно, захотим сравнить изменения.

Давайте посмотрим, как мы можем выполнить тестирование пользовательского интерфейса компонентов React с помощью библиотеки response-testing.

Выбор и тестирование элементов DOM

React-Testing-Library отображает компоненты React как браузер, и мы можем выбирать элементы так же, как мы можем использовать DOM API или на вкладке браузера Inspector.

Один из способов выбора элемента - использовать функцию getByText.

getByText

Эта функция захватывает текст с экрана. Он возвращает текстовый узел текста, и с его помощью мы можем использовать его для утверждений или для взаимодействия с пользователем.

Пример:

У нас есть компонент приложения:

import React from 'react';
function App() {
    return (
        <div>
            React Example
        </div>
    );
}
export default App;

Давайте создадим тестовый файл, чтобы убедиться, что компонент приложения отображает на экране текст React Example. Для этого мы создаем файл App.test.js с кодом:

import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
describe('App', () => {
    test('renders App component', () => {
        render(<App />);
        screen.getByText('React Example');
    });
});

Что мы здесь делали? Мы импортировали модуль React, а также импортировали рендер и экран из «@ testing-library / react». Эти функции являются основными для тестирования компонентов React. render отображает компонент React в виртуальном браузере, а экран используется для доступа к визуализированной DOM.

Затем мы импортировали компонент нашего приложения, создали набор тестов и тест. Здесь мы визуализировали наш компонент App с помощью функции рендеринга и использовали метод getByText на экране, чтобы получить текст «React Example» из компонента.

screen.getByText возвращает сопоставители, которые мы можем использовать для различных утверждений.

  • toBeNull
  • toBeInTheDocument
  • toBeDisabled
  • toBeEnabled
  • toBeEmpty
  • toBeEmptyDOMElement
  • toBeInvalid
  • toBeRequired
  • toBeValid
  • toBeVisible
  • toContainElement
  • toContainHTML
  • toHaveAttribute
  • toHaveClass
  • toHaveFocus
  • toHaveFormValues
  • toHaveStyle
  • toHaveTextContent
  • toHaveValue
  • toHaveDisplayValue
  • быть проверенным
  • toBeParfullyChecked
  • toHaveDescription

Продемонстрируем несколько:

toBeNull: утверждает, что текстовый узел имеет значение NULL, т.е. не существует.

import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
describe('App', () => {
    test('renders App component', () => {
        render(<App />);
        // fails
        expect(screen.getByText('React Example')).toBeNull();
    });
});

Вышеупомянутый тест завершится неудачно, потому что есть текстовый узел со значением «React Example» в DOM, отображаемый нашим компонентом App.

toBeInTheDocument

Это проверяет, присутствует ли текстовый узел в DOM, отображаемом компонентом.

import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
describe('App', () => {
    test('renders App component', () => {
        render(<App />);
        // succeeds
        expect(screen.getByText('React Example')).toBeInTheDocument();
    });
});

Это удается, потому что «Пример реакции» находится в DOM приложения.

getByTestId

Функция рендеринга также возвращает функции, которые мы можем использовать для дальнейших утверждений о рендеринге компонента. Один из них - getByTestId.

эта функция возвращает экземпляр DOM элемента с указанным атрибутом набора данных «data-testid».

Пример:

import React from 'react';
function App() {
    return (
        <div data-testid="mainDiv">
            <h3 data-testid="mainHeader">
                React Example
            </h3>
            <h3 data-testid="sideHeader">
                React Example 2
            </h3>
        </div>
    );
}
export default App;

Хорошо, у нас есть элементы с атрибутом data-testid. С их помощью мы можем использовать getByTestId для выбора элементов, передавая значение их data-testid функции.

import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
describe('App', () => {
    test('renders App component', () => {
        const { getTestById } = render(<App />);
        // succeeds
        expect(getTestById("mainHeader")).toHaveTextContent("React Example")
        // succeeds
        expect(getTestById("sideHeader")).toHaveTextContent("React Example 2")
    });
});

Функция сопоставления toHaveTextContent проверяет, есть ли у элемента дочерний текстовый узел с переданным значением. Итак, «mainHeader» и «sideHeader» успешны, потому что в элементах есть тексты «React Example» и «React Example 2».

Тестирование событий

Мы можем протестировать события DOM в наших компонентах React с помощью библиотеки react-testing-library.

События DOM, такие как:

  • щелкнуть
  • события мыши
  • ключевые события
  • сенсорные события
  • так далее

Давайте проведем рефакторинг нашего компонента приложения, чтобы мы могли протестировать события кликов.

import React, { useState } from 'react';
function App() {
    const { state, setState } = useState(0)
    return (
        <div>
            <div>
                State: <span data-testid="display">{state}</span>
            </div>
            <div>
                <button data-testid="button1" onClick={setState(1)}>1</button>
                <button data-testid="button2" onClick={setState(2)}>2</button>
                <button data-testid="button3" onClick={setState(3)}>3</button>
            </div>
        </div>
    );
}
export default App;

Наш компонент приложения имеет состояние state с начальным значением 0. У нас есть три кнопки 1, 2, 3, которые при нажатии изменяют состояние на 1, 2 или 3 соответственно.

Посмотрим на тест:

import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import App from './App';
describe('App', () => {
    test('renders App component', () => {
        const { getByTestId } = render(<App />);
        expect(getByTestId('display')).toHaveTextContent('0')
        fireEvent.click(getByTestId('button1'))
        expect(getByTestId('display')).toHaveTextContent('1')
        fireEvent.click(getByTestId('button2'))
        expect(getByTestId('display')).toHaveTextContent('2')
        fireEvent.click(getByTestId('button3'))
        expect(getByTestId('display')).toHaveTextContent('3')
    });
});

Во-первых, чтобы использовать события, мы импортировали fireEvent из библиотеки тестирования-реакции. Поскольку мы тестируем события щелчка, мы будем вызывать метод click из объекта fireEvent.

Первоначально «дисплей» будет отображен как «0». Итак, тест прошел успешно.

Затем мы нажимаем кнопку «button1». Это изменит состояние на 1, и «display» будет иметь «1» в качестве текстового содержимого, поэтому тест будет успешным.

Затем мы нажимаем кнопку «button2», это установит состояние на 2, так что тест на «отображение» текстового содержимого будет истинным успешно.

В последнюю очередь мы нажимаем на «button3», и состояние устанавливается на 3. Итак, тест на «display» с текстом «3» проходит. Итак, наконец, все наши тесты пройдут.

На самом деле это не щелчок, а запуск события щелчка по событиям. Так же, как мы можем сделать это программно.

button1.dispatchEvent(new Event("click"))

Асинхронное тестирование

Асинхронные действия можно протестировать в компонентах React с помощью библиотеки response-testing.

В этом примере мы используем функцию setTimeout, чтобы показать, как мы можем использовать react-testing-library для тестирования асинхронных действий.

Асинхронные действия нарушают поток выполнения в JavaScript, поэтому библиотеки тестирования могут дождаться их завершения, прежде чем запускать утверждения для разрешенных значений.

Вот пример:

import React, { useState } from 'react';
function App() {
    const { state, setState } = useState(0)
    const setStateAsync = (value) => {
        setTimeout(() => {
            setState(value)
        }, 1000)
    }
    return (
        <div>
            <div>
                State: <span data-testid="display">{state}</span>
            </div>
            <div>
                <button data-testid="button1" onClick={setStateAsync(1)}>Button1</button>
                <button data-testid="button2" onClick={setStateAsync(2)}>Button2</button>
                <button data-testid="button3" onClick={setStateAsync(3)}>Button3</button>
            </div>
        </div>
    );
}
export default App;

Все еще используется наш предыдущий пример, но на этот раз он устанавливает состояние асинхронно с помощью setTimeout.

При нажатии любой из кнопок setStateAsync установит тайм-аут, а остальная часть кода завершится. Теперь, когда истекает время, равное 1 секунде, состояние устанавливается и представление обновляется. Поскольку это асинхронный интерфейс, пользовательский интерфейс не будет зависать в течение всего времени, он выполнит завершение, а система цикла событий включится и выполнит обработчик функции setTimeout.

import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import App from './App';
describe('App', () => {
    test('renders App component', () => {
        const { getByTestId } = render(<App />);
        expect(getByTestId('display')).toHaveTextContent('0')
        await fireEvent.click(getByTestId('button1'))
        expect(getByTestId('display')).toHaveTextContent('1')
        await fireEvent.click(getByTestId('button2'))
        expect(getByTestId('display')).toHaveTextContent('2')
        await fireEvent.click(getByTestId('button3'))
        expect(getByTestId('display')).toHaveTextContent('3')
    });
});

Мы добавили ключевое слово await в fireEvent.click, чтобы дождаться тайм-аута setTimeout и установить состояние перед его продолжением.

Первое утверждение проверяет, что «дисплей» будет иметь начальное текстовое содержимое «0». Затем запускает событие «click» на «button1» и ждет его возврата. По возвращении «дисплей» будет установлен, затем он утверждает, что «дисплей» имеет текстовое содержание «2», и так до последнего.

Видеть? Асинхронное тестирование в компонентах React с использованием библиотеки react-testing-library очень просто.

Заключение

Тестирование в React с использованием библиотеки react-testing-library - это очень простой процесс. Enzyme подходит ближе, но библиотека react-testing-library делает тестирование похожим на человека и может тестировать ваше поведение пользователей, потому что люди будут использовать ваши продукты.

Не стесняйтесь спрашивать, есть ли у вас какие-либо вопросы или комментарии, в разделе комментариев.

Спасибо !!!

Учить больше