mcpField
F
frontend-testing

frontend-testing

github.com/exceptionless测试与质量2.5k42 次安装1 天前更新
代码仓库

关于 frontend-testing

面向使用 Vitest 和 @testing-library/svelte 的前端团队的测试指南,说明测试文件位置、TDD 与 AAA 实践、可访问查询的优先级及模块模拟示例。

主要功能

  1. 01Vitest 与 Testing Library 配置
  2. 02测试文件就近放置规范
  3. 03TDD 和 Arrange-Act-Assert 模式
  4. 04可访问查询的选择顺序
  5. 05模块模拟示例

适用场景

  1. 01编写 Svelte 组件测试
  2. 02修复缺陷时补充测试
  3. 03选择可访问的测试查询

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/exceptionless/exceptionless --skill frontend-testing

安装后在 Claude Code 中输入/frontend-testing确认已加载。

详细介绍

前端测试

文档: vitest.dev | testing-library.com

框架与位置

  • 框架: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();
    });
});

查询选择优先级

使用可访问性查询(而非实现细节):

  1. screen.getByRole("button", { name: /submit/i }) — 基于角色(首选)
  2. screen.getByLabelText("Email address") — 基于标签
  3. screen.getByText("Welcome back") — 基于文本
  4. screen.getByTestId("complex-chart") — 测试 ID(备用)
  5. ❌ 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