React очень популярен при создании фронтенд-компонентов, и когда масштаб вашего приложения увеличивается, нам необходимо иметь надежные тесты, чтобы охватить все сценарии. Компонент модульного тестирования становится отличным способом обеспечить качество приложения и легко найти ошибки во время сборки, если тесты написаны хорошо. В этом посте мы узнаем, как протестировать компонент с помощью React и Vitest.
- Что мы будем строить?
- Что такое Vite?
- Почему стоит использовать Vitest?
- Что такое HMR?
- Инициализация проекта React с помощью Vite
- Добавление Vitest для тестирования
- Конфигурация Vitest
- Создание компонента Accordion
- Создание теста в vitest
- Добавление тестового скрипта
- Запустите сценарий теста
- Добавление конфигурации для Vitest
- Установка зависимых компонентов для тестирования
- Добавление конфигурации для Vitest
- Напишите модульный тест, чтобы проверить, виден ли текст в vitest
- Создание теста для скрытия и показа содержимого
- Реализация логики для прохождения теста
- Добавление стилей для аккордеона
- Написание теста для проверки поведения открытия/закрытия
- Заключение
Что мы будем строить?
Мы собираемся создать простой компонент аккордеона в React и написать модульные тесты в Vitest. У компонента будет два состояния. Первое — свернутое состояние, в котором отображается только заголовок. Другое состояние — открытое, в котором отображается заголовок и содержимое под заголовком.
Что такое Vite?
Vite — это инструмент сборки, который прост в использовании и быстро компилирует проект react. Мы будем использовать Vite, поскольку с ним легко интегрировать инструмент тестирования Vitest. Если вы хотите узнать основы работы с Vite, мы рассказали об этом в этой статье.
Почему стоит использовать Vitest?
Vitest действительно быстр и имеет хороший опыт разработчика при использовании с Vite. Мы можем совместно использовать конфигурацию vite с vitest, что упрощает процесс, а также гарантирует, что среда тестирования похожа на среду сборки. Vitest поддерживает HMR, который действительно ускоряет рабочий процесс.
Что такое HMR?
HMR расшифровывается как Hot Module Reloading. При любом изменении кода, только изменения обновляются на сервере, и сервер отражает новые изменения.
Скорость отображения изменений на вашем сервере повышается, поскольку мы отправляем на перезагрузку только частичные изменения, а не перезагружаем весь код.
Теперь, когда все жаргонизмы убраны с дороги, давайте посмотрим на код, ради которого вы пришли.
Инициализация проекта React с помощью Vite
Мы можем инициализировать проект с помощью следующей команды
npm init vite
cd react-vite-vitest
npm install
Добавление Vitest для тестирования
Мы можем добавить Vitest, чтобы начать добавлять тесты в проект. Установите Vitest в качестве dev-зависимости.
npm install -D vitest
Конфигурация Vitest
Одним из преимуществ Vitest является то, что он использует тот же конфиг, что и Vite. Это гарантирует, что тестовое окружение совпадает с окружением сборки, что повышает надежность тестов.
Мы обновим конфиг следующим кодом, чтобы добавить js-dom
, который помогает в тестировании
/// <reference types="vitest" />
/// <reference types="vite/client" />
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
}
})
Создание компонента Accordion
Создайте новый компонент под названием Accordion.tsx
и добавьте следующий код для создания простого компонента аккордеона. Он еще не завершен, и мы завершим его, добавив сначала тестирование
import React from "react";
type AccordionProps = {
title: string;
children: React.ReactNode;
}
const Accordion = (props: AccordionProps) => {
const {title, children} = props;
return (
<div className="accordion">
<h3 className="accordion-title">{title}</h3>
<div className="accordion-content">
{children}
</div>
</div>
);
}
export default Accordion;
Мы просто берем title
и children
и отображаем их. Компонент аккордеона должен иметь возможность сжиматься и разжиматься при нажатии на кнопку. Поэтому давайте сначала добавим тестовый пример для этой функции, а затем реализуем ее.
Создание теста в vitest
Создайте новый файл Accordion.test.tsx
, который будет содержать тест для компонента Accordion. Добавьте в этот файл следующий код
import {describe, test} from 'vitest';
describe("Accordion test", () => {
test("Should show title", () => {
})
})
Давайте разберем приведенный выше код
- describe — Используется для группировки тестов и используется для описания того, что в данный момент тестируется
- test — Отдельный тест, который запускается Vitest. Он может либо пройти, либо не пройти.
Здесь мы не добавили ни одного теста, который бы возвращал true или false. Мы сделаем это вскоре после добавления тестового скрипта.
Добавление тестового скрипта
Нам нужно добавить команду vitest
в package.json, чтобы запустить сценарий тестирования.
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"test": "vitest"
},
Просто вызвав команду vitest
, начнется выполнение теста, который будет находиться в режиме watch
. Это означает, что любые изменения, вносимые в файл, приведут к повторному запуску теста.
Запустите сценарий теста
npm run test
Поскольку у нас нет оператора expect, тест считается пройденным.
Добавление конфигурации для Vitest
Для правильного тестирования компонентов react нам необходимо, чтобы функциональность DOM была воспроизведена в тестовой среде.
JSDom помогает получить такое окружение для тестирования, поэтому нам нужно установить его в качестве dev-зависимости.
Мы также будем использовать testing-library
, которая поможет нам получить больше полезных функций для тестирования компонентов. Из этого пакета мы получим такие вещи, как функция render
, которая будет имитировать отображение компонента в браузере.
Установка зависимых компонентов для тестирования
npm i -D jsdom @testing-library/react
Добавление конфигурации для Vitest
/// <reference types="vitest" />
/// <reference types="vite/client" />
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
}
})
Напишите модульный тест, чтобы проверить, виден ли текст в vitest
import {describe, expect, test} from 'vitest';
import {render, screen} from '@testing-library/react';
import Accordion from './Accordion';
describe("Accordion test", () => {
test("should show title all the time", () => {
render(<Accordion title='Testing'><h4>Content</h4></Accordion>);
expect(screen.getByText(/Testing/i)).toBeDefined()
})
})
Это первый базовый тест, который проверяет, что заголовок всегда отображается на экране. Мы используем некоторые функции из testing-library
, такие как render
и screen.getByText
.
getByText возвращает элемент, если он найден, в противном случае будет выброшено исключение, что приведет к неудаче в тестировании.
Существует множество других полезных функций, которые можно выбрать в зависимости от вашего случая использования.
https://testing-library.com/docs/react-testing-library/api
Создание теста для скрытия и показа содержимого
Нам нужно отобразить компонент в каждом тестовом примере. В этом случае мы можем использовать beforeEach
, который будет запускать код внутри перед каждым тестом.
import {beforeEach, describe, expect, test} from 'vitest';
import {render, screen} from '@testing-library/react';
import Accordion from './Accordion';
describe("Accordion", () => {
beforeEach(() => {
render(<Accordion title='Testing'><h4>Content</h4></Accordion>);
});
test("should show title all the time", () => {
expect(screen.getByText(/Testing/i)).toBeDefined()
})
test("should not show the content at the start", () => {
expect(screen.getByText(/Content/i)).toBeUndefined()
})
})
Второй тест сейчас должен быть неудачным, потому что мы ожидаем, что содержимое не должно быть показано в начале, но мы не реализовали код для этого. Это хороший пример того, как работает TDD (Test Driven Development). Сначала мы пишем тест, который не пройдет, а затем реализуем функциональность, чтобы он прошел.
Реализация логики для прохождения теста
import React, { useState } from "react";
import './Accordion.css'
type AccordionProps = {
title: string;
children: React.ReactNode;
}
const Accordion = (props: AccordionProps) => {
const {title, children} = props;
const [show, setShow] = useState(false);
const onAccordionClick = () => {
setShow(!show);
}
return (
<div className="accordion">
<div className="accordion-title">
<h3>{title}</h3>
<button onClick={() => onAccordionClick()}>{!show ? 'Show' : 'Hide'}</button>
</div>
{show && (
<div>
{children}
</div>
)}
</div>
);
}
export default Accordion;
Мы добавляем код для скрытия и отображения содержимого аккордеона. Это достигается простым изменением переменной состояния show
.
Мы устанавливаем начальное значение show
в false, что позволит пройти тест.
Теперь, когда у нас есть базовая функция аккордеона, давайте сосредоточимся на создании дополнительных стилей с помощью CSS.
Добавление стилей для аккордеона
.accordion {
width: 80vw;
border: 1px solid gray;
border-radius: 5px;
}
.accordion-title {
padding: 0px 25px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid gray;
}
Написание теста для проверки поведения открытия/закрытия
Мы завершили работу над функциональностью Accordion. Теперь мы можем добавить еще один тест, чтобы проверить, открывается ли аккордеон при нажатии на кнопку.
Давайте напишем 3-й тест следующим образом
fireEvent
из библиотеки тестирования помогает имитировать действия пользователя в модульном тесте. Мы используем метод click
для нажатия на кнопку. Это должно вызвать открытие аккордеона, после чего мы ожидаем выполнения действия. Поскольку это будет асинхронное действие, мы используем ключевое слово await
.
Асинхронный модульный тест будет иметь таймаут по умолчанию, и он будет ждать до этого времени. По истечении этого времени тест будет провален.
import {beforeEach, describe, expect, test} from 'vitest';
import {fireEvent, render, screen, waitFor} from '@testing-library/react';
import Accordion from './Accordion';
import "@testing-library/jest-dom";
import { act } from 'react-dom/test-utils';
describe("Accordion", () => {
beforeEach(() => {
render(<Accordion title='Testing'><h4>Content</h4></Accordion>);
});
test("should show title all the time", () => {
expect(screen.getByText(/Testing/i)).toBeInTheDocument();
})
test("should not show the content at the start", () => {
expect(screen.queryByText(/Content/i)).not.toBeInTheDocument();
})
test("should show the content on accordion click",async () => {
const title = screen.getByText(/Show/i);
fireEvent.click(title)
expect(await screen.findByText(/Content/i)).toBeInTheDocument();
})
})
Заключение
Мы узнали, как писать модульные тесты с помощью Vitest в React. Vitest все еще находится на стадии бета-версии и пока не готов к использованию в производстве. Мы считаем, что у Vitest огромный потенциал и он выглядит как хорошая альтернатива Jest, который используется многими разработчиками.
Сообщите нам, если вы работали с Vitest, и мы будем рады любым отзывам на этот пост.
Присоединяйтесь к нашему Discord — https://discord.gg/AUjrcK6eep