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

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

Qwik City рекомендует использовать Vitest для unit-тестов и тестирования компонентов, и Playwright для E2E тестирования.

Окно терминала
npm run qwik add vitest
vitest.config.ts
import { defineConfig } from 'vitest/config';
import { qwikVite } from '@builder.io/qwik/optimizer';
export default defineConfig({
plugins: [qwikVite()],
test: {
globals: true,
environment: 'node',
},
});
src/utils/format.ts
export function formatPrice(price: number, currency = 'RUB'): string {
return new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency,
}).format(price);
}
// src/utils/format.test.ts
import { describe, it, expect } from 'vitest';
import { formatPrice } from './format';
describe('formatPrice', () => {
it('форматирует рублёвую цену', () => {
expect(formatPrice(1000)).toBe('1 000 ₽');
});
it('форматирует цену в долларах', () => {
expect(formatPrice(99.99, 'USD')).toContain('99.99');
});
it('обрабатывает ноль', () => {
expect(formatPrice(0)).toBe('0 ₽');
});
});

Qwik предоставляет createDOM для тестирования компонентов без браузера:

src/components/counter.test.ts
import { createDOM } from '@builder.io/qwik/testing';
import { describe, it, expect } from 'vitest';
import { Counter } from './counter';
describe('Counter', () => {
it('рендерит начальное значение', async () => {
const { screen, render } = await createDOM();
await render(<Counter initialValue={5} />);
expect(screen.querySelector('[data-test="count"]')?.textContent).toBe('5');
});
it('увеличивает счётчик при клике', async () => {
const { screen, render, userEvent } = await createDOM();
await render(<Counter initialValue={0} />);
const button = screen.querySelector('[data-test="increment"]')!;
await userEvent(button, 'click');
expect(screen.querySelector('[data-test="count"]')?.textContent).toBe('1');
});
});
src/routes/blog/[slug]/index.test.ts
import { describe, it, expect, vi } from 'vitest';
import { createRequestEvent } from '@builder.io/qwik-city/testing';
// Мокаем БД
vi.mock('../../../lib/db', () => ({
db: {
posts: {
findUnique: vi.fn(),
},
},
}));
describe('usePost loader', () => {
it('возвращает пост по slug', async () => {
const mockPost = { id: '1', title: 'Test', slug: 'test' };
vi.mocked(db.posts.findUnique).mockResolvedValue(mockPost);
const event = createRequestEvent({ params: { slug: 'test' } });
const result = await usePost.call(event);
expect(result).toEqual(mockPost);
});
it('возвращает 404 если пост не найден', async () => {
vi.mocked(db.posts.findUnique).mockResolvedValue(null);
const event = createRequestEvent({ params: { slug: 'nonexistent' } });
await expect(usePost.call(event)).rejects.toThrow();
});
});
Окно терминала
npm run qwik add playwright
tests/home.spec.ts
import { test, expect } from '@playwright/test';
test('главная страница загружается', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveTitle(/Мой Qwik App/);
await expect(page.locator('h1')).toBeVisible();
});
test('счётчик работает', async ({ page }) => {
await page.goto('/counter');
const count = page.locator('[data-test="count"]');
const increment = page.locator('[data-test="increment"]');
await expect(count).toHaveText('0');
await increment.click();
await expect(count).toHaveText('1');
});
test('форма отправляется', async ({ page }) => {
await page.goto('/contact');
await page.fill('[name="email"]', '[email protected]');
await page.fill('[name="message"]', 'Привет!');
await page.click('[type="submit"]');
await expect(page.locator('.success-message')).toBeVisible();
});