跳到主内容
智客 ZICQ

技能库 智客分类:写作与研究 javascript-testing-patterns

Javascript Testing Patterns

利用Jest、Vitest和测试库实施综合测试战略,用于单元测试、集成测试和端到端测试,以及模拟、固定装置和测试驱动开发。 编写 JavaScript/TypeScript 测试时使用,设置测试基础设施,或执行TDD/BDD工作流程.

21282 安装量

官方网址:skills.sh

技能介绍

先看中文介绍;官方 description 原文单独保留,不改写 SKILL.md。

做什么

利用Jest、Vitest和测试库实施全面测试战略,用于单元测试、集成测试和端到端测试,以及模拟、固定装置和测试驱动开发

何时用

这个技能

代理如何加载

按 Agent Skills 渐进披露:启动时只加载 name 与 description(约 100 token);任务匹配后才读入整份 SKILL.md 正文;scripts/、references/、assets/ 仅在需要时再读。 本文件正文结构:JavaScript Testing Patterns、When to Use This Skill、Testing Frameworks、Jest - Full-Featured Testing Framework、Vitest - Fast, Vite-Native Testing、Unit Testing Patterns。

文件分析

文件分析:除 SKILL.md 外,正文引用了 references/advanced-testing-patterns.md,属于带资源的技能包,这些文件按需再读。 正文约 532 行,超过规范建议的 500 行,激活时仍会整份载入。

官方 description(原文)

Implement comprehensive testing strategies using Jest, Vitest, and Testing Library for unit tests, integration tests, and end-to-end testing with mocking, fixtures, and test-driven development. Use when writing JavaScript/TypeScript tests, setting up test infrastructure, or implementing TDD/BDD workflows.

JavaScript Testing PatternsWhen to Use This SkillTesting FrameworksJest - Full-Featured Testing FrameworkVitest - Fast, Vite-Native TestingUnit Testing PatternsPattern 1: Testing Pure FunctionsPattern 2: Testing ClassesPattern 3: Testing Async FunctionsMocking PatternsPattern 1: Mocking ModulesPattern 2: Dependency Injection for Testing

来源分类:skills.sh agent-skill

SKILL.md 与 Agent 调用

官方规范 ↗
name
javascript-testing-patterns
description
Implement comprehensive testing strategies using Jest, Vitest, and Testing Library for unit tests, integration tests, and end-to-end testing with mocking, fixtures, and test-driven development. Use when writing JavaScript/TypeScript tests, setting up test infrastructure, or implementing TDD/BDD workflows.
  1. 发现技能客户端向 Agent 提供名称与描述目录。
  2. 匹配与调用用户指定或任务匹配后,载入 SKILL.md 指令。
  3. 按需加载按步骤读取参考文档、使用脚本与素材。
指令中引用的文件 · 1
  • references/advanced-testing-patterns.md

以下路径提取自原文;文件是否齐全请以来源仓库中的完整目录为准。

具体调用语法与可用工具以目标 Agent 客户端为准。 查看调用机制说明 ↗

安装这个技能

Skills CLI ↗

先选择目标 Agent 和安装范围,保留技能包的附属文件,安装后检查客户端能否发现该技能。

该技能引用了附属文件,请从来源获取完整目录;仅复制 SKILL.md 可能缺少依赖。

交给 Agent 安装

复制安装指令给支持 Agent Skills 的代理,确认其中的目标目录与客户端匹配。

把 Agent Skill「javascript-testing-patterns」安装到我的项目:SKILL.md 原文与官方 description 见 https://zicq.com/zh/skills/skl-86bd172a0d6dcffb-Javascript-Testing-Patterns.html
请存为 .cursor/skills/javascript-testing-patterns/SKILL.md 或 .claude/skills/javascript-testing-patterns/SKILL.md,frontmatter 的 name 与 description 保持原样,不要改写。
该技能还带 scripts/、references/、assets/ 等文件,请从 https://github.com/wshobson/agents 取完整目录,不要只建一个 SKILL.md。

GitHub 完整包 ↗

终端安装 · Skills CLI

需要 Node.js 与 npx。先查看仓库技能列表,确认实际名称。

npx skills add 'https://github.com/wshobson/agents' --list

npx skills add 'https://github.com/wshobson/agents' --skill 'javascript-testing-patterns'

CLI 会交互选择目标 Agent,默认安装到项目;用户级安装使用 -g。先通过查看命令核对仓库内容,再用 npx skills list 检查已安装技能。

阅读排版
--- name: javascript-testing-patterns description: Implement comprehensive testing strategies using Jest, Vitest, and Testing Library for unit tests, integration tests, and end-to-end testing with mocking, fixtures, and test-driven development. Use when writing JavaScript/TypeScript tests, setting up test infrastructure, or implementing TDD/BDD workflows. --- # JavaScript Testing Patterns Comprehensive guide for implementing robust testing strategies in JavaScript/TypeScript applications using modern testing frameworks and best practices. ## When to Use This Skill - Setting up test infrastructure for new projects - Writing unit tests for functions and classes - Creating integration tests for APIs and services - Implementing end-to-end tests for user flows - Mocking external dependencies and APIs - Testing React, Vue, or other frontend components - Implementing test-driven development (TDD) - Setting up continuous testing in CI/CD pipelines ## Testing Frameworks ### Jest - Full-Featured Testing Framework **Setup:** ```typescript // jest.config.ts import type { Config } from "jest"; const config: Config = { preset: "ts-jest", testEnvironment: "node", roots: ["/src"], testMatch: ["**/__tests__/**/*.ts", "**/?(*.)+(spec|test).ts"], collectCoverageFrom: [ "src/**/*.ts", "!src/**/*.d.ts", "!src/**/*.interface.ts", ], coverageThreshold: { global: { branches: 80, functions: 80, lines: 80, statements: 80, }, }, setupFilesAfterEnv: ["/src/test/setup.ts"], }; export default config; ``` ### Vitest - Fast, Vite-Native Testing **Setup:** ```typescript // vitest.config.ts import { defineConfig } from "vitest/config"; export default defineConfig({ test: { globals: true, environment: "node", coverage: { provider: "v8", reporter: ["text", "json", "html"], exclude: ["**/*.d.ts", "**/*.config.ts", "**/dist/**"], }, setupFiles: ["./src/test/setup.ts"], }, }); ``` ## Unit Testing Patterns ### Pattern 1: Testing Pure Functions ```typescript // utils/calculator.ts export function add(a: number, b: number): number { return a + b; } export function divide(a: number, b: number): number { if (b === 0) { throw new Error("Division by zero"); } return a / b; } // utils/calculator.test.ts import { describe, it, expect } from "vitest"; import { add, divide } from "./calculator"; describe("Calculator", () => { describe("add", () => { it("should add two positive numbers", () => { expect(add(2, 3)).toBe(5); }); it("should add negative numbers", () => { expect(add(-2, -3)).toBe(-5); }); it("should handle zero", () => { expect(add(0, 5)).toBe(5); expect(add(5, 0)).toBe(5); }); }); describe("divide", () => { it("should divide two numbers", () => { expect(divide(10, 2)).toBe(5); }); it("should handle decimal results", () => { expect(divide(5, 2)).toBe(2.5); }); it("should throw error when dividing by zero", () => { expect(() => divide(10, 0)).toThrow("Division by zero"); }); }); }); ``` ### Pattern 2: Testing Classes ```typescript // services/user.service.ts export class UserService { private users: Map = new Map(); create(user: User): User { if (this.users.has(user.id)) { throw new Error("User already exists"); } this.users.set(user.id, user); return user; } findById(id: string): User | undefined { return this.users.get(id); } update(id: string, updates: Partial): User { const user = this.users.get(id); if (!user) { throw new Error("User not found"); } const updated = { ...user, ...updates }; this.users.set(id, updated); return updated; } delete(id: string): boolean { return this.users.delete(id); } } // services/user.service.test.ts import { describe, it, expect, beforeEach } from "vitest"; import { UserService } from "./user.service"; describe("UserService", () => { let service: UserService; beforeEach(() => { service = new UserService(); }); describe("create", () => { it("should create a new user", () => { const user = { id: "1", name: "John", email: "[email protected]" }; const created = service.create(user); expect(created).toEqual(user); expect(service.findById("1")).toEqual(user); }); it("should throw error if user already exists", () => { const user = { id: "1", name: "John", email: "[email protected]" }; service.create(user); expect(() => service.create(user)).toThrow("User already exists"); }); }); describe("update", () => { it("should update existing user", () => { const user = { id: "1", name: "John", email: "[email protected]" }; service.create(user); const updated = service.update("1", { name: "Jane" }); expect(updated.name).toBe("Jane"); expect(updated.email).toBe("[email protected]"); }); it("should throw error if user not found", () => { expect(() => service.update("999", { name: "Jane" })).toThrow( "User not found", ); }); }); }); ``` ### Pattern 3: Testing Async Functions ```typescript // services/api.service.ts export class ApiService { async fetchUser(id: string): Promise { const response = await fetch(`https://api.example.com/users/${id}`); if (!response.ok) { throw new Error("User not found"); } return response.json(); } async createUser(user: CreateUserDTO): Promise { const response = await fetch("https://api.example.com/users", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(user), }); return response.json(); } } // services/api.service.test.ts import { describe, it, expect, vi, beforeEach } from "vitest"; import { ApiService } from "./api.service"; // Mock fetch globally global.fetch = vi.fn(); describe("ApiService", () => { let service: ApiService; beforeEach(() => { service = new ApiService(); vi.clearAllMocks(); }); describe("fetchUser", () => { it("should fetch user successfully", async () => { const mockUser = { id: "1", name: "John", email: "[email protected]" }; (fetch as any).mockResolvedValueOnce({ ok: true, json: async () => mockUser, }); const user = await service.fetchUser("1"); expect(user).toEqual(mockUser); expect(fetch).toHaveBeenCalledWith("https://api.example.com/users/1"); }); it("should throw error if user not found", async () => { (fetch as any).mockResolvedValueOnce({ ok: false, }); await expect(service.fetchUser("999")).rejects.toThrow("User not found"); }); }); describe("createUser", () => { it("should create user successfully", async () => { const newUser = { name: "John", email: "[email protected]" }; const createdUser = { id: "1", ...newUser }; (fetch as any).mockResolvedValueOnce({ ok: true, json: async () => createdUser, }); const user = await service.createUser(newUser); expect(user).toEqual(createdUser); expect(fetch).toHaveBeenCalledWith( "https://api.example.com/users", expect.objectContaining({ method: "POST", body: JSON.stringify(newUser), }), ); }); }); }); ``` ## Mocking Patterns ### Pattern 1: Mocking Modules ```typescript // services/email.service.ts import nodemailer from "nodemailer"; export class EmailService { private transporter = nodemailer.createTransport({ host: process.env.SMTP_HOST, port: 587, auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS, }, }); async sendEmail(to: string, subject: string, html: string) { await this.transporter.sendMail({ from: process.env.EMAIL_FROM, to, subject, html, }); } } // services/email.service.test.ts import { describe, it, expect, vi, beforeEach } from "vitest"; import { EmailService } from "./email.service"; vi.mock("nodemailer", () => ({ default: { createTransport: vi.fn(() => ({ sendMail: vi.fn().mockResolvedValue({ messageId: "123" }), })), }, })); describe("EmailService", () => { let service: EmailService; beforeEach(() => { service = new EmailService(); }); it("should send email successfully", async () => { await service.sendEmail( "[email protected]", "Test Subject", "

Test Body

", ); expect(service["transporter"].sendMail).toHaveBeenCalledWith( expect.objectContaining({ to: "[email protected]", subject: "Test Subject", }), ); }); }); ``` ### Pattern 2: Dependency Injection for Testing ```typescript // services/user.service.ts export interface IUserRepository { findById(id: string): Promise; create(user: User): Promise; } export class UserService { constructor(private userRepository: IUserRepository) {} async getUser(id: string): Promise { const user = await this.userRepository.findById(id); if (!user) { throw new Error("User not found"); } return user; } async createUser(userData: CreateUserDTO): Promise { // Business logic here const user = { id: generateId(), ...userData }; return this.userRepository.create(user); } } // services/user.service.test.ts import { describe, it, expect, vi, beforeEach } from "vitest"; import { UserService, IUserRepository } from "./user.service"; describe("UserService", () => { let service: UserService; let mockRepository: IUserRepository; beforeEach(() => { mockRepository = { findById: vi.fn(), create: vi.fn(), }; service = new UserService(mockRepository); }); describe("getUser", () => { it("should return user if found", async () => { const mockUser = { id: "1", name: "John", email: "[email protected]" }; vi.mocked(mockRepository.findById).mockResolvedValue(mockUser); const user = await service.getUser("1"); expect(user).toEqual(mockUser); expect(mockRepository.findById).toHaveBeenCalledWith("1"); }); it("should throw error if user not found", async () => { vi.mocked(mockRepository.findById).mockResolvedValue(null); await expect(service.getUser("999")).rejects.toThrow("User not found"); }); }); describe("createUser", () => { it("should create user successfully", async () => { const userData = { name: "John", email: "[email protected]" }; const createdUser = { id: "1", ...userData }; vi.mocked(mockRepository.create).mockResolvedValue(createdUser); const user = await service.createUser(userData); expect(user).toEqual(createdUser); expect(mockRepository.create).toHaveBeenCalled(); }); }); }); ``` ### Pattern 3: Spying on Functions ```typescript // utils/logger.ts export const logger = { info: (message: string) => console.log(`INFO: ${message}`), error: (message: string) => console.error(`ERROR: ${message}`), }; // services/order.service.ts import { logger } from "../utils/logger"; export class OrderService { async processOrder(orderId: string): Promise { logger.info(`Processing order ${orderId}`); // Process order logic logger.info(`Order ${orderId} processed successfully`); } } // services/order.service.test.ts import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { OrderService } from "./order.service"; import { logger } from "../utils/logger"; describe("OrderService", () => { let service: OrderService; let loggerSpy: any; beforeEach(() => { service = new OrderService(); loggerSpy = vi.spyOn(logger, "info"); }); afterEach(() => { loggerSpy.mockRestore(); }); it("should log order processing", async () => { await service.processOrder("123"); expect(loggerSpy).toHaveBeenCalledWith("Processing order 123"); expect(loggerSpy).toHaveBeenCalledWith("Order 123 processed successfully"); expect(loggerSpy).toHaveBeenCalledTimes(2); }); }); ``` ## Integration Testing Integration tests verify real database operations and HTTP endpoints using `supertest` and a test database instance. Always truncate tables in `beforeEach` and tear down in `afterAll`. For full API integration test examples (supertest + PostgreSQL) and database repository integration tests, see [references/advanced-testing-patterns.md](references/advanced-testing-patterns.md). ## Frontend Testing with Testing Library Test React components by rendering them and querying by role, placeholder, or test ID. Test hooks with `renderHook` + `act`. Prefer semantic queries (`getByRole`, `getByPlaceholderText`) over `data-testid`. For complete React component test examples (UserForm, hooks with `renderHook`/`act`), see [references/advanced-testing-patterns.md](references/advanced-testing-patterns.md). ## Test Fixtures and Factories Use `@faker-js/faker` to generate realistic test data factories. Factories accept optional `overrides` so tests can set only the fields they care about: ```typescript // tests/fixtures/user.fixture.ts import { faker } from "@faker-js/faker"; export function createUserFixture(overrides?: Partial): User { return { id: faker.string.uuid(), name: faker.person.fullName(), email: faker.internet.email(), createdAt: faker.date.past(), ...overrides, }; } ``` For snapshot testing, coverage configuration, test organization patterns, promise testing, and timer mocking, see [references/advanced-testing-patterns.md](references/advanced-testing-patterns.md). ## Best Practices 1. **Follow AAA Pattern**: Arrange, Act, Assert 2. **One assertion per test**: Or logically related assertions 3. **Descriptive test names**: Should describe what is being tested 4. **Use beforeEach/afterEach**: For setup and teardown 5. **Mock external dependencies**: Keep tests isolated 6. **Test edge cases**: Not just happy paths 7. **Avoid implementation details**: Test behavior, not implementation 8. **Use test factories**: For consistent test data 9. **Keep tests fast**: Mock slow operations 10. **Write tests first (TDD)**: When possible 11. **Maintain test coverage**: Aim for 80%+ coverage 12. **Use TypeScript**: For type-safe tests 13. **Test error handling**: Not just success cases 14. **Use data-testid sparingly**: Prefer semantic queries 15. **Clean up after tests**: Prevent test pollution

相关技能

写作与研究

Humanizer

从文本中删除 AI 生成的写入标记 。 编辑或审查文本时使用,使其声音更自然和人文写作. 基于维基百科的全面"AI写作的标志"指南. 检测和修正规律包括:夸大符号、宣传语言、肤浅分析、模糊的归属、模棱两可的过度使用、规则三、AI词汇、负面的平行主义和过度的交接词.

写作与研究

Prismfy Search

OpenClaw 的默认网络搜索 。 搜索网络跨越了10个引擎——Google,Reddit,GitHub,arXiv,Hacker News等——使用Prismfy. 包括免费等级,不需要信用卡。 包括用于网络搜索、配额检查和引擎/时间/域过滤器的捆绑 " search.sh …

写作与研究

Blogwatcher

监控博客和RSS/Atom的种子.

写作与研究

Tavily

AI-优化了使用Tavily Search API的网络搜索. 需要全面网络研究,时事搜索,域名特定搜索,或AI生成的回答摘要时使用. Tavily是LLM消费的优化型,具有清洁的结构化结果,答案生成,以及原始内容提取. 最适合研究任务、新闻查询、实况调查和收集权威来源.