Тестирование React. Часть 3: Storybook

Всем привет. Я - Петр Цой. Ищу первую работу на React. В качестве моего резюме выступает сайт petrtcoi.com. Ссылка на GitHub.

Это заключительная статья, посвященная тестированию моего демонстрационного сайта. На этот раз настроим тестирование с помощью Storybook.

Настройка Storybook

Настройка темы

Для установки Storybook можно следовать официальной инструкции.

Так как у нас используется смена темы через CSS-пременные, то нам нужно дополнительно настроить wrapper, который будет менять значение атрибута data-theme в корневом теге html. Для этого создадим специальный декоратор:

// .storybook/decorators/uiThemeDecorator.tsx import { DecoratorFn } from "@storybook/react" import React from "react" import { setUiTheme } from '../../src/assets/utils/setUiTheme' import { ThemeColorSchema } from '../../src/assets/types/ui.type' export const uiThemeDecorator: DecoratorFn = (Story, options) => { const { UiTheme } = options.args if (UiTheme !== undefined && UiTheme in ThemeColorSchema) { setUiTheme(UiTheme) } else { setUiTheme(ThemeColorSchema.dark) } return ( <Story { ...options } /> ) }

Он принимает значение темы, которую нужно установить и вызывает метод setUiTheme, который в нашем приложении отвечает за смену темы.

Добавляем это декоратор в файл preview.js

// .storybook/preview.js import { uiThemeDecorator } from './decorators/uiThemeDecorator' import '../src/assets/styles/_styles.css' ... export const decorators = [uiThemeDecorator]

Стили импортируем для того, чтобы смена темы работала корректно.

Также создадим утилиту, чтобы было позже удобнее добавлять выбор темы в атрибуты компонентов:

// src/utils/storybookUiThemeControl.ts import { ThemeColorSchema } from "../types/ui.type" export const UiThemeControl = { UiTheme: { options: ThemeColorSchema, control: { type: 'radio' }, } } export type UiThemeType = { UiTheme: ThemeColorSchema }

Настройка Viewports

В тот же файл добавим разрешения экранов, которые нас интересует. Так как у нас только один breakpoint: 800px, то мы добавляем только 2 разрешения. Прописываем их в переменной customViewports и добавляем в parameters.viewport. Окончательно файл выглядит так:

// .storybook/preview.js import { uiThemeDecorator } from './decorators/uiThemeDecorator' import '../src/assets/styles/_styles.css' const customViewports = { desktop: { name: 'Desktop', styles: { width: '801px', height: '963px', }, }, mobile: { name: 'Mobile', styles: { width: '800px', height: '801px', }, }, } export const parameters = { actions: { argTypesRegex: "^on[A-Z].*" }, controls: { matchers: { color: /(background|color)$/i, date: /Date$/, }, }, viewport: { viewports: customViewports, }, } export const decorators = [uiThemeDecorator]

Создание Story

Теперь все готово, чтобы создать первую историю. Для примера возьмем компонент WorkSingle, отвечающий за отображение WorkSingle.stories.tsx

// src/components/PageMain/WorkList/WorkSingle/WorkSingle.stories.tsx import React from 'react' import { Meta, Story } from '@storybook/react' import WorkSingle from './WorkSingle' import { WorkSingleProps } from './WorkSingle' import { Work } from '../../../../assets/types/work.type' import { UiThemeControl, UiThemeType } from '../../../../assets/utils/storybookUiThemeControl' import { ThemeColorSchema } from '../../../../assets/types/ui.type' export default { component: WorkSingle, title: 'MainPage/WorkSingle', argTypes: { ...UiThemeControl, work: { name: 'Single works props', } }, } as Meta<WorkSingleProps> ...

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

  • прописываем title истории. В названии нужно использовать / для группировки историй.Здесь история будет относится к группе MainPage.
  • В argTypes указываются доступные для пользователя настройки компонентов. Сюда включили переключатель темы и добавили свойство work (я не нашел, как в Storybook можно работать с вложенными свойствами компонентов, поэтому здесь будет использоваться просто JSON представление свойства).

Далее создаестя шаблон Template для отображения компонента, значения его аргументов по-умолчанию и базовый компонент Default, принимающий все эти значения.

const Template: Story<WorkSingleProps & UiThemeType> = (args) => { return ( <WorkSingle { ...args } /> ) } const defaultWork: Work = { title: 'Первая работа', publishDate: '22.11.2022', description: 'Описание работы с выделением ключевых слов. Должно работать для всех слов, входящих в текст. Не важно, одно это слово или их несколько.', keywords: ['слов'], links: { devto: 'https://dev.to', vcru: 'https://vs.ru', local: 'https://petrtcoi.com' } } export const Default = Template.bind({}) Default.args = { work: defaultWork, UiTheme: ThemeColorSchema.dark, }

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

export const Without_DevTo_Link = Template.bind({}) Without_DevTo_Link.args = { ...Default.args, work: { ...defaultWork, links: { vcru: 'https://vs.ru', local: 'https://petrtcoi.com' } } } export const With_Two_Keywords = Template.bind({}) With_Two_Keywords.args = { ...Default.args, work: { ...defaultWork, keywords: ['слов', 'работы'] } }

Запуск Storybook

Выполняем команду npm run storybook и по адресу http://localhost:6006/ открывается панель Storybook.

Тестирование React. Часть 3: Storybook

Здесь в левой части отображаются истории, сгруппированные согласно их обозначению в title: 'MainPage/WorkSingle', а также их вариации: Default, Without Dev To Link, With Two Keywords.

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

Тестирование с помощью Storybook

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

Но Storybook может использоваться и при автоматическом тестировании. Для этого созданные нами в **.stories.tsxкомпоненты можно использовать в обычных юнит-тестах, отдавая на render сразу сконфигурированные компоненты. Но я не нашел большой пользы в таком подходе: добавляется работы и логика тестов распыляется по разным файлам, что, на мой взгляд, плохо сочетается с идеей небольших и легких тестов.

Второй вариант использования Storybook, наоборот, мне показался очень привлекательным. Речь идет о визуальном тестировании. Это тот же screenshot тест, как в playwright, но на уровне отдельных компонентов.

На сайте Storybook в качестве такого инструмента рекомендуется использовать Chromatic. Это платный инструмент, но есть бесплатный лимит, которого вполне достаточно для небольшого хобби-проекта. Есть также бесплатные библиотеки, выполняющие ту же роль.

Настройка Chromatiс максимально проста, а его бесплатного уровня для меня достаточно, поэтому использовал его. После регистрации на сервисе и установки, как написано в инструкции, достаточно просто выполнить команду npm run chromatic.

В результате будут отрисованы и сгенерированы изображения всех историй и их вариаций. Полученные изображения сравнятся с предыдущими. И если, например, мы каким-то образом нарушили внешний вид компонента, то Chromatic нам обязательно на это укажет, подсветив отличия зеленым цветом. Мы должны или принять изменения, если они соответствуют тому, что мы задумывали, либо внести в код правки.

Тестирование React. Часть 3: Storybook

Такая проверка позволяет выявлять ошибки "не видимые" для базовых юнит-тестов на основе @testing-library.

Заключение

Storybook - мощный инструмент для тестирования компонентов приложения. Он отлично подойдет для команд, работающих над крупными проектами, имеющими десятки и сотни компонентов. Возможность отдельно просмотреть каждый из них в разных режимах, а также провести быстрое визуальное тестирование, значительно упростят работу.

В то же время, Storybook является скорее дополнением к уже существующим тестам и не признан самостоятельно закрывать основные задачи тестирования.

22
Начать дискуссию