手记

Svelte 最新中文文档教程(19)—— 测试

前言

Svelte,一个语法简洁、入门容易,面向未来的前端框架。从 Svelte 诞生之初,就备受开发者的喜爱,根据统计,从 2019 年到 2024 年,连续 6 年一直是开发者最感兴趣的前端框架 No.1

Svelte 以其独特的编译时优化机制著称,具有轻量级高性能易上手等特性,非常适合构建轻量级 Web 项目,也是我做个人项目的首选技术栈。

目前 Svelte 基于 Svelte 5 发布了最新的官方文档,但却缺少对应的中文文档。为了帮助大家学习 Svelte,为爱发电翻译了官方文档。

我同时搭建了 Svelte 最新的中文文档站点:https://svelte.yayujs.com ,如果需要辅助学习,也可以入手我的小册《Svelte 开发指南》,语法篇、实战篇、原理篇三大篇章带你系统掌握 Svelte!

虽说是翻译,但个人并不喜欢严格遵守原文,为了保证中文阅读流畅,会删减部分语句,对难懂的部分也会另做补充解释,希望能给大家带来一个好的中文学习体验。

测试

测试可以帮助您编写和维护代码,防止出现回归问题。测试框架可以帮助您完成这一目标,通过让您能够描述对代码行为的断言或期望。

Svelte 对于您使用哪种测试框架并不持有特定立场 — 您可以使用 VitestJasmineCypressPlaywright 等解决方案来编写单元测试、集成测试和端到端测试。

使用 Vitest 进行单元和集成测试

单元测试允许您测试代码中的小型独立部分。集成测试允许您测试应用程序的各个部分,以查看它们是否协同工作。如果您使用 Vite(包括通过 SvelteKit),我们推荐使用 Vitest

首先安装 Vitest:

npm install -D vitest

然后调整您的 vite.config.js

/// file: vite.config.js
import { defineConfig } from +++'vitest/config'+++;

export default defineConfig({
  // ...
  // 告诉 Vitest 使用 `package.json` 文件中的 `browser` 入口点,即使它在 Node 中运行
  resolve: process.env.VITEST
    ? {
        conditions: ['browser']
      }
    : undefined
});

[!NOTE] 如果加载所有包的浏览器版本不适合您的需求,例如您还需要测试后端库,您可能需要使用别名配置

现在您可以为 .js/.ts 文件中的代码编写单元测试:

/// file: multiplier.svelte.test.js
import { flushSync } from 'svelte';
import { expect, test } from 'vitest';
import { multiplier } from './multiplier.js';

test('Multiplier', () => {
	let double = multiplier(0, 2);

	expect(double.value).toEqual(0);

	double.set(5);

	expect(double.value).toEqual(10);
});

在测试文件中使用符文(runes)

您可以在测试文件中使用符文。首先确保您的打包工具知道在运行测试之前要通过 Svelte 编译器处理文件,方法是在文件名中添加 .svelte(例如 multiplier.svelte.test.js)。之后,您就可以在测试中使用符文了。

/// file: 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);
});

如果被测试的代码使用了 effects,您需要将测试包装在 $effect.root 中:

/// file: 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);

		// logger 使用 $effect 来记录其输入的更新
		let log = logger(() => count);

		// 效果通常在微任务后运行,
		// 使用 flushSync 同步执行所有待处理的效果
		flushSync();
		expect(log.value).toEqual([0]);

		count = 1;
		flushSync();

		expect(log.value).toEqual([0, 1]);
	});

	cleanup();
});

组件测试

您可以使用 Vitest 在隔离环境下测试您的组件。

[!NOTE] 在编写组件测试之前,请考虑是否真的需要测试组件本身,还是更多地关注组件内部的逻辑。如果是后者,考虑将逻辑提取出来单独测试,避免组件测试的开销

首先安装 jsdom(一个提供 DOM API 模拟的库):

npm install -D jsdom

然后调整您的 vite.config.js

/// file: vite.config.js
import { defineConfig } from 'vitest/config';

export default defineConfig({
	plugins: [
		/* ... */
	],
	test: {
		// 如果您在客户端测试组件,您需要设置 DOM 环境。
		// 如果不是所有文件都需要这个环境,您可以在测试文件顶部使用
		// `// @vitest-environment jsdom` 注释来代替。
		environment: 'jsdom'
	},
	// 告诉 Vitest 使用 `package.json` 文件中的 `browser` 入口点,即使它在 Node 中运行
	resolve: process.env.VITEST
		? {
				conditions: ['browser']
			}
		: undefined
});

之后,您可以创建一个测试文件,在其中导入要测试的组件,以编程方式与之交互并编写关于结果的预期:

/// file: component.test.js
import { flushSync, mount, unmount } from 'svelte';
import { expect, test } from 'vitest';
import Component from './Component.svelte';

test('Component', () => {
	// 使用 Svelte 的 `mount` API 实例化组件
	const component = mount(Component, {
		target: document.body, // 由于 jsdom 的存在,`document` 是可用的
		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 这样的工具可以帮助简化测试。上面的测试可以重写为:

/// file: 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 包含一些示例

使用 Playwright 进行端到端测试

E2E(端到端的简称)测试允许您从用户的视角测试整个应用程序。本节以 Playwright 为例,但您也可以使用其他解决方案,如 CypressNightwatchJS

要开始使用 Playwright,可以通过 VS Code 扩展 安装,或者使用 npm init playwright 从命令行安装。它也是运行 npx sv create 时设置 CLI 的一部分。

完成后,您应该有一个 tests 文件夹和一个 Playwright 配置。您可能需要调整该配置,告诉 Playwright 在运行测试之前要做什么 - 主要是在某个端口启动您的应用程序:

/// file: 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 交互并编写断言。

// @errors: 2307 7031
/// file: tests/hello-world.spec.js
import { expect, test } from '@playwright/test';

test('主页包含预期的 h1', async ({ page }) => {
	await page.goto('/');
	await expect(page.locator('h1')).toBeVisible();
});

Svelte 中文文档

本篇已收录在专栏 《Svelte 中文文档》,该系列预计 40 篇。

系统学习 Svelte,欢迎入手小册《Svelte 开发指南》。语法篇、实战篇、原理篇三大篇章带你系统掌握 Svelte!

此外我还写过 JavaScript 系列TypeScript 系列React 系列Next.js 系列冴羽答读者问等 14 个系列文章, 全系列文章目录:https://github.com/mqyqingfeng/Blog

0人推荐
随时随地看视频
慕课网APP