Тестирование React-приложения с помощью Vitest

React очень популярен при создании фронтенд-компонентов, и когда масштаб вашего приложения увеличивается, нам необходимо иметь надежные тесты, чтобы охватить все сценарии. Компонент модульного тестирования становится отличным способом обеспечить качество приложения и легко найти ошибки во время сборки, если тесты написаны хорошо. В этом посте мы узнаем, как протестировать компонент с помощью React и 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", () => {

    })
})
Вход в полноэкранный режим Выйти из полноэкранного режима

Давайте разберем приведенный выше код

  1. describe — Используется для группировки тестов и используется для описания того, что в данный момент тестируется
  2. 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

Оцените статью
devanswers.ru
Добавить комментарий