测试
本教程共 50 篇 · 第 39 篇 · 更新于 2026-08-05 · 约 5 分钟阅读
本节目标:了解 Svelte 应用的三种测试方式,学会用 Vitest 测试响应式逻辑、用 mount 测试组件、用 Playwright 做端到端测试。
三种测试类型
Svelte 应用通常有三层测试,从细到粗:
| 类型 | 测什么 | 工具 | 速度 |
|---|---|---|---|
| 单元测试 | 纯函数、响应式逻辑 | Vitest | 快 |
| 组件测试 | 组件渲染和交互 | Vitest + jsdom | 中 |
| E2E 测试 | 完整用户流程 | Playwright | 慢 |
Tip不是所有逻辑都要放在组件里。数据转换、状态管理这些逻辑提取到
.svelte.js文件中,更容易测试,覆盖率也更高。
Vitest 单元测试
Vitest 是 Vite 生态的测试框架,和 SvelteKit 无缝集成。安装:
npm install -D vitest
配置 vite.config.js:
import { defineConfig } from 'vitest/config';
export default defineConfig({
resolve: process.env.VITEST
? { conditions: ['browser'] }
: undefined
});
conditions: ['browser'] 让 Vitest 在 Node 中使用包的浏览器入口,这对 Svelte 组件很关键。
测试 .svelte.js 中的逻辑
把响应式逻辑放在 .svelte.js 文件中,然后直接测试:
// multiplier.svelte.ts
export function multiplier(initial: number, k: number) {
let count = $state(initial);
return {
get value() {
return count * k;
},
set: (c: number) => {
count = c;
}
};
}
// multiplier.svelte.test.js
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);
});
在测试文件中使用 Runes
如果测试文件名包含 .svelte(如 xxx.svelte.test.js),你可以在测试里直接使用 Runes:
// counter.svelte.test.js
import { flushSync } from 'svelte';
import { expect, test } from 'vitest';
test('reactive state', () => {
let count = $state(0);
let double = $derived(count * 2);
expect(double).toEqual(0);
count = 5;
flushSync();
expect(double).toEqual(10);
});
测试 $effect
测试包含 $effect 的代码时,需要用 $effect.root 包裹,并用 flushSync 强制执行副作用:
// logger.svelte.test.js
import { flushSync } from 'svelte';
import { expect, test } from 'vitest';
import { logger } from './logger.svelte.js';
test('effect logging', () => {
const cleanup = $effect.root(() => {
let count = $state(0);
let log = logger(() => count);
// flushSync 强制执行排队的 effect
flushSync();
expect(log).toEqual([0]);
count = 1;
flushSync();
expect(log).toEqual([0, 1]);
});
cleanup();
});
Note
$effect默认在微任务中执行。测试需要同步断言,所以用flushSync()把排队的副作用立即执行。
组件测试
组件测试需要 DOM 环境。先用 jsdom 模拟浏览器 API:
npm install -D jsdom
配置 Vitest 使用 jsdom:
// vite.config.js
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'jsdom'
},
resolve: process.env.VITEST
? { conditions: ['browser'] }
: undefined
});
用 mount/unmount 测试
Svelte 5 的 mount 和 unmount API 可以直接在测试中挂载组件:
// component.test.js
import { flushSync, mount, unmount } from 'svelte';
import { expect, test } from 'vitest';
import Counter from './Counter.svelte';
test('Counter increments', () => {
const component = mount(Counter, {
target: document.body,
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>');
// 清理
unmount(component);
});
用 @testing-library/svelte
@testing-library/svelte 提供了更高层的 API,让测试更贴近用户视角:
npm install -D @testing-library/svelte @testing-library/user-event
// component.test.js
import { render, screen } from '@testing-library/svelte';
import userEvent from '@testing-library/user-event';
import { expect, test } from 'vitest';
import Counter from './Counter.svelte';
test('Counter increments', async () => {
const user = userEvent.setup();
render(Counter);
const button = screen.getByRole('button');
expect(button).toHaveTextContent('0');
await user.click(button);
expect(button).toHaveTextContent('1');
});
Tip推荐用
@testing-library/svelte。它不关心组件内部实现,只测试用户看到的内容,测试更稳定。
E2E 测试
端到端测试模拟真实用户操作整个应用。用 Playwright 是当前主流方案:
npm init 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 的存在,只和浏览器交互:
// tests/home.spec.js
import { expect, test } from '@playwright/test';
test('首页显示标题', async ({ page }) => {
await page.goto('/');
await expect(page.locator('h1')).toBeVisible();
});
测试策略选择
| 场景 | 推荐方式 |
|---|---|
| 测试纯函数逻辑 | 单元测试,提取到 .svelte.js |
| 测试组件渲染和交互 | @testing-library/svelte |
| 测试响应式更新 | flushSync + 直接断言 |
| 测试包含 effect 的逻辑 | $effect.root + flushSync |
| 测试完整用户流程 | Playwright E2E |
Note不要测试 Svelte 框架本身的功能(比如”$state 变了模板会不会更新”)。Svelte 有自己的测试套件。你应该测的是你的业务逻辑和组件行为。
本节回顾
- 三种测试:单元测试(Vitest)、组件测试(Vitest + jsdom)、E2E 测试(Playwright)
.svelte.js文件中的逻辑可以直接被 Vitest 测试- 测试文件名含
.svelte时可在测试中使用 Runes - 测试
$effect需要$effect.root包裹 +flushSync强制执行 - 组件测试可用
mount/unmount底层 API 或@testing-library/svelte高层 API - E2E 测试用 Playwright,完全不知道 Svelte 的存在,只模拟用户操作