关于 frontend-testing
面向使用 Vitest 和 @testing-library/svelte 的前端团队的测试指南,说明测试文件位置、TDD 与 AAA 实践、可访问查询的优先级及模块模拟示例。
主要功能
- 01Vitest 与 Testing Library 配置
- 02测试文件就近放置规范
- 03TDD 和 Arrange-Act-Assert 模式
- 04可访问查询的选择顺序
- 05模块模拟示例
适用场景
- 01编写 Svelte 组件测试
- 02修复缺陷时补充测试
- 03选择可访问的测试查询
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ exceptionless/ exceptionless --skill frontend-testing安装后在 Claude Code 中输入/frontend-testing确认已加载。
详细介绍
前端测试
框架与位置
- 框架:Vitest + @testing-library/svelte
- 位置:与代码放在一起,文件名为
.test.ts或.spec.ts - TDD 工作流:修复 Bug 或添加功能时,先编写一个会失败的测试
AAA 模式
明确划分 Arrange(准备)、Act(执行)、Assert(断言)区域:
import { describe, expect, it } from "vitest";
describe("Calculator", () => {
it("should add two numbers correctly", () => {
// Arrange
const a = 5;
const b = 3;
// Act
const result = add(a, b);
// Assert
expect(result).toBe(8);
});
});
使用 Spy 进行测试
import { beforeEach, describe, expect, it, vi } from "vitest";
import { CachedPersistedState } from "./cached-persisted-state.svelte";
describe("CachedPersistedState", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("should return cached value without reading storage repeatedly", () => {
// Arrange
const getItemSpy = vi.spyOn(Storage.prototype, "getItem");
localStorage.setItem("test-key", "value1");
const state = new CachedPersistedState("test-key", "default");
getItemSpy.mockClear();
// Act
const val1 = state.current;
const val2 = state.current;
// Assert
expect(val1).toBe("value1");
expect(val2).toBe("value1");
expect(getItemSpy).not.toHaveBeenCalled();
});
});
查询选择优先级
使用可访问性查询(而非实现细节):
screen.getByRole("button", { name: /submit/i })— 基于角色(首选)screen.getByLabelText("Email address")— 基于标签screen.getByText("Welcome back")— 基于文本screen.getByTestId("complex-chart")— 测试 ID(备用)- ❌
screen.getByClassName("btn-primary")— 切勿使用实现细节
模拟模块
import { vi, describe, it, beforeEach, expect } from "vitest";
import { render, screen } from "@testing-library/svelte";
vi.mock("$lib/api/organizations", () => ({
getOrganizations: vi.fn(),
}));
import { getOrganizations } from "$lib/api/organizations";
import OrganizationList from "./organization-list.svelte";
describe("OrganizationList", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("displays organizations from API", async () => {
// Arrange
vi.mocked(getOrganizations).mockResolvedValue([{ id: "1", name: "Org One" }]);
// Act
render(OrganizationList);
// Assert
expect(await screen.findByText("Org One")).toBeInTheDocument();
});
});
评论
支持 Markdown