Перейти к содержимому

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

Тестирование помогает вам писать и поддерживать ваш код, а также защищает от регрессий. Тестовые фреймворки помогают вам в этом, позволяя описывать утверждения или ожидания о том, как должен вести себя ваш код. Svelte не навязывает вам использование конкретного тестового фреймворка — вы можете писать модульные тесты, интеграционные тесты и тесты сквозного взаимодействия, используя такие решения, как Vitest, Jasmine, Cypress и Playwright.

Модульные тесты позволяют вам тестировать небольшие изолированные части вашего кода. Интеграционные тесты позволяют вам тестировать части вашего приложения, чтобы увидеть, работают ли они вместе. Если вы используете Vite (в том числе через SvelteKit), мы рекомендуем использовать Vitest.

Чтобы начать, установите Vitest:

Окно терминала
npm i -D vitest

Затем настройте ваш vite.config.js:

vite.config.js
import { defineConfig } from 'vitest/config';
export default defineConfig({
// ...
// Указываем Vitest использовать точки входа `browser` в файлах `package.json`, даже если он работает в Node
resolve: process.env.VITEST
? {
conditions: ['browser']
}
: undefined
});

Теперь вы можете писать модульные тесты для кода внутри ваших файлов .js/.ts:

multiplier.svelte.test.js
import { flushSync } from 'svelte';
import { expect, test } from 'vitest';
import { multiplier } from './multiplier.svelte.js';
test('Multiplier', () => {
let double = multiplier(0, 2);
expect(double.value).toEqual(0);
double.set(5);
expect(double.value).toEqual(10);
});
multiplier.svelte.js
/**
* @param {number} initial
* @param {number} k
*/
export function multiplier(initial, k) {
let count = $state(initial);
return {
get value() {
return count * k;
},
/** @param {number} c */
set: (c) => {
count = c;
}
};
}

Использование рун в ваших тестовых файлах

Заголовок раздела «Использование рун в ваших тестовых файлах»

Поскольку Vitest обрабатывает ваши тестовые файлы так же, как и ваши исходные файлы, вы можете использовать руны внутри ваших тестов, если имя файла включает .svelte:

multiplier.svelte.test.js
import { flushSync } from 'svelte';
import { expect, test } from 'vitest';
import { multiplier } from './multiplier.svelte.js';
test('Multiplier', () => {
let count = $state(0);
let double = multiplier(() => count, 2);
expect(double.value).toEqual(0);
count = 5;
expect(double.value).toEqual(10);
});
multiplier.svelte.js
/**
* @param {() => number} getCount
* @param {number} k
*/
export function multiplier(getCount, k) {
return {
get value() {
return getCount() * k;
}
};
}

Если тестируемый код использует эффекты, вам нужно обернуть тест внутри $effect.root:

logger.svelte.test.js
import { flushSync } from 'svelte';
import { expect, test } from 'vitest';
import { logger } from './logger.svelte.js';
test('Effect', () => {
const cleanup = $effect.root(() => {
let count = $state(0);
// логгер использует `$effect` для регистрации обновлений своего входного значения
let log = logger(() => count);
// эффекты обычно выполняются после микрозадачи,
// используйте `flushSync`, чтобы выполнить все ожидающие эффекты синхронно
flushSync();
expect(log).toEqual([0]);
count = 1;
flushSync();
expect(log).toEqual([0, 1]);
});
cleanup();
});
logger.svelte.js
/**
* @param {() => any} getValue
*/
export function logger(getValue) {
/** @type {any[]} */
let log = [];
$effect(() => {
log.push(getValue());
});
return log;
}

Можно тестировать ваши компоненты изолированно, что позволяет отображать их в браузере (настоящем или смоделированном), имитировать поведение и делать проверки, не запуская всё приложение целиком.

Чтобы начать, установите jsdom (библиотеку, которая эмулирует API DOM):

Окно терминала
npm i -D jsdom

Затем настройте ваш vite.config.js:

vite.config.js
import { defineConfig } from 'vitest/config';
export default defineConfig({
plugins: [
/* ... */
],
test: {
// Если вы тестируете компоненты на стороне клиента, вам нужно настроить среду DOM.
// Если не все ваши файлы должны иметь эту среду, используйте
// комментарий `// @vitest-environment jsdom` в начале тестовых файлов.
environment: 'jsdom'
},
// Указываем Vitest использовать точки входа `browser` в файлах `package.json`, даже если он работает в Node
resolve: process.env.VITEST
? {
conditions: ['browser']
}
: undefined
});

После этого вы можете создать файл теста, в котором вы импортируете компонент для тестирования, взаимодействуете с ним программно и пишете ожидания о результатах:

component.test.js
import { flushSync, mount, unmount } from 'svelte';
import { expect, test } from 'vitest';
import Component from './Component.svelte';
test('Component', () => {
// Создаём экземпляр компонента, используя API `mount` от Svelte
const component = mount(Component, {
target: document.body, // `document` существует благодаря jsdom
props: { initial: 0 }
});
expect(document.body.innerHTML).toBe('<button>0</button>');
// Нажимаем кнопку, затем выполняем сброс изменений, чтобы можно было синхронно записать ожидания
document.body.querySelector('button').click();
flushSync();
expect(document.body.innerHTML).toBe('<button>1</button>');
// Удаляем компонент из DOM
unmount(component);
});

Хотя процесс очень простой, он также является низкоуровневым и несколько хрупким, так как точная структура вашего компонента может часто меняться. Инструменты, такие как @testing-library/svelte, могут помочь упростить ваши тесты. Вышеуказанный тест можно переписать следующим образом:

component.test.js
import { render, screen } from '@testing-library/svelte';
import userEvent from '@testing-library/user-event';
import { expect, test } from 'vitest';
import Component from './Component.svelte';
test('Component', async () => {
const user = userEvent.setup();
render(Component);
const button = screen.getByRole('button');
expect(button).toHaveTextContent(0);
await user.click(button);
expect(button).toHaveTextContent(1);
});

При написании тестов компонентов, которые включают двусторонние привязки, контекст или свойства сниппетов, лучше создать обёртку для вашего конкретного теста и взаимодействовать с ней. @testing-library/svelte содержит некоторые примеры.

Storybook — это инструмент для разработки и документирования UI-компонентов, который также можно использовать для тестирования ваших компонентов. Они запускаются в браузерном режиме Vitest, который отображает ваши компоненты в реальном браузере для создания максимально приближенной к реальности среды тестирования.

Чтобы начать работу, сначала установите Storybook (с помощью CLI Svelte) в ваш проект с помощью команды npx sv add storybook и выберите рекомендуемую конфигурацию, которая включает функции тестирования. Если вы уже используете Storybook, а также для получения дополнительной информации о возможностях тестирования Storybook, следуйте документации по тестированию Storybook, чтобы начать работу.

Вы можете создавать сторисы для различных вариантов компонентов и тестировать взаимодействия с помощью play-функции, которая позволяет имитировать поведение и делать проверки, используя API Testing Library и Vitest. Вот пример двух сторисов, которые можно протестировать: один отображает компонент LoginForm в пустом состоянии, а другой имитирует заполнение формы пользователем:

LoginForm.stories.svelte
<script module>
import { defineMeta } from '@storybook/addon-svelte-csf';
import { expect, fn } from 'storybook/test';
import LoginForm from './LoginForm.svelte';
const { Story } = defineMeta({
component: LoginForm,
args: {
// Передаем мок-функцию в проп `onSubmit`
onSubmit: fn(),
}
});
</script>
<Story name="Очистить форму" />
<Story
name="Заполненная форма"
play={async ({ args, canvas, userEvent }) => {
// Имитируем заполнение формы пользователем
await userEvent.type(canvas.getByTestId('email'), 'email@provider.com');
await userEvent.type(canvas.getByTestId('password'), 'a-random-password');
await userEvent.click(canvas.getByRole('button'));
// Запускаем проверки
await expect(args.onSubmit).toHaveBeenCalledTimes(1);
await expect(canvas.getByText('Вы вошли!')).toBeInTheDocument();
}}
/>

E2E (сокращение от «end to end») тесты позволяют вам тестировать ваше приложение целиком, с точки зрения пользователя. В этом разделе используется Playwright в качестве примера, но вы также можете использовать другие решения, такие как Cypress или NightwatchJS.

Чтобы начать работу с Playwright, установите его через расширение для VS Code или с помощью команды npm init playwright. Он также является частью настройки CLI, когда вы запускаете npx sv create.

После этого у вас должна появиться папка tests и конфигурация Playwright. Вам может понадобиться настроить эту конфигурацию, чтобы указать Playwright, что делать перед запуском тестов — в основном, запустить ваше приложение на определённом порту:

playwright.config.js
const config = {
webServer: {
command: 'npm run build && npm run preview',
port: 4173
},
testDir: 'tests',
testMatch: /(.+\.)?(test|spec)\.[jt]s/
};
export default config;

Теперь вы можете начать писать тесты. Эти тесты полностью не зависят от Svelte как фреймворка, поэтому вы в основном взаимодействуете с DOM и пишете утверждения.

tests/hello-world.spec.js
import { expect, test } from '@playwright/test';
test('home page has expected h1', async ({ page }) => {
await page.goto('/');
await expect(page.locator('h1')).toBeVisible();
});