Token导航 LogoToken导航TokenDH.com
前端设计执行命令github未标认证来源可访问clear审计未展示

unit-testing-expert单元测试专家

Agent Skill

用于辅助前端页面、组件、样式和交互逻辑的开发与维护。它适合让 Agent 生成或审查 React、Next.js、Vue、Tailwind、CSS 等相关代码,整理组件结构,或定位布局和性能问题。使用时需要结合项目现有设计系统、路由和构建方式,避免只生成孤立片段;涉及页面改动时,应配合本地预览和构建检查确认视觉效果。

总安装

490

周安装

20

GitHub Stars

127

下载量

157
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

3

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:unit-testing-expert(单元测试专家)
来源仓库:https://github.com/anton-abyzov/specweave
仓库路径:skills/unit-testing-expert
安装命令:
npx skills add https://github.com/anton-abyzov/specweave --skill unit-testing-expert
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/anton-abyzov/specweave --skill unit-testing-expert

简介

用于辅助前端页面、组件、样式和交互逻辑的开发与维护。

  • 适合生成或审查 React、Next.js、Vue、Tailwind、CSS 等相关代码。
  • 使用时需结合项目现有设计系统、路由和构建方式,避免生成孤立片段。
  • 涉及页面改动时应配合本地预览和构建检查确认视觉效果。
  • unit-testing-expert 属于前端设计类 Skill,可作为该场景下的辅助能力补充。

SKILL.md

Unit Testing Expert

Self-contained unit testing expertise for Vitest/Jest in ANY user project.


Test-Driven Development (TDD)

Red-Green-Refactor Cycle:

// 1. RED: Write failing test
describe('Calculator', () => {
  it('should add two numbers', () => {
    const calc = new Calculator();
    expect(calc.add(2, 3)).toBe(5);
  });
});

// 2. GREEN: Minimal implementation
class Calculator {
  add(a: number, b: number): number {
    return a + b;
  }
}

// 3. REFACTOR: Improve code
class Calculator {
  add(...numbers: number[]): number {
    return numbers.reduce((sum, n) => sum + n, 0);
  }
}

TDD Benefits:

  • Better design (testable code)
  • Living documentation
  • Faster debugging
  • Higher confidence

Vitest/Jest Fundamentals

Basic Test Structure

import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { UserService } from './UserService';

describe('UserService', () => {
  let service: UserService;

  beforeEach(() => {
    service = new UserService();
  });

  afterEach(() => {
    vi.clearAllMocks();
  });

  it('should create user', () => {
    const user = service.create({ name: 'John', email: 'john@test.com' });

    expect(user).toMatchObject({
      id: expect.any(String),
      name: 'John',
      email: 'john@test.com'
    });
  });

  it('should throw for invalid email', () => {
    expect(() => {
      service.create({ name: 'John', email: 'invalid' });
    }).toThrow('Invalid email');
  });
});

Async Testing

it('should fetch user from API', async () => {
  const user = await api.fetchUser('user-123');

  expect(user).toEqual({
    id: 'user-123',
    name: 'John Doe'
  });
});

// Testing async errors
it('should handle API errors', async () => {
  await expect(api.fetchUser('invalid')).rejects.toThrow('User not found');
});

Mocking Strategies

1. Mock Functions

// Mock a function
const mockFn = vi.fn();
mockFn.mockReturnValue(42);
expect(mockFn()).toBe(42);

// Mock with implementation
const mockAdd = vi.fn((a, b) => a + b);
expect(mockAdd(2, 3)).toBe(5);

// Verify calls
expect(mockFn).toHaveBeenCalledTimes(1);
expect(mockFn).toHaveBeenCalledWith(expected);

2. Mock Modules

// Mock entire module
vi.mock('./database', () => ({
  query: vi.fn().mockResolvedValue([{ id: 1, name: 'Test' }])
}));

import { query } from './database';

it('should fetch users from database', async () => {
  const users = await query('SELECT * FROM users');
  expect(users).toHaveLength(1);
});

3. Spies

// Spy on existing method
const spy = vi.spyOn(console, 'log');

myFunction();

expect(spy).toHaveBeenCalledWith('Expected message');
spy.mockRestore();

4. Mock Dependencies

class UserService {
  constructor(private db: Database) {}

  async getUser(id: string) {
    return this.db.query('SELECT * FROM users WHERE id = ?', [id]);
  }
}

// Test with mock
const mockDb = {
  query: vi.fn().mockResolvedValue({ id: '123', name: 'John' })
};

const service = new UserService(mockDb);
const user = await service.getUser('123');

expect(mockDb.query).toHaveBeenCalledWith(
  'SELECT * FROM users WHERE id = ?',
  ['123']
);

Test Patterns

AAA Pattern (Arrange-Act-Assert)

it('should calculate total price', () => {
  // Arrange
  const cart = new ShoppingCart();
  cart.addItem({ price: 10, quantity: 2 });
  cart.addItem({ price: 5, quantity: 3 });

  // Act
  const total = cart.getTotal();

  // Assert
  expect(total).toBe(35);
});

Given-When-Then (BDD)

describe('Shopping Cart', () => {
  it('should apply discount when total exceeds $100', () => {
    // Given: A cart with items totaling $120
    const cart = new ShoppingCart();
    cart.addItem({ price: 120, quantity: 1 });

    // When: Getting the total
    const total = cart.getTotal();

    // Then: 10% discount applied
    expect(total).toBe(108); // $120 - $12 (10%)
  });
});

Parametric Testing

describe.each([
  [2, 3, 5],
  [10, 5, 15],
  [-1, 1, 0],
  [0, 0, 0]
])('Calculator.add(%i, %i)', (a, b, expected) => {
  it(`should return ${expected}`, () => {
    const calc = new Calculator();
    expect(calc.add(a, b)).toBe(expected);
  });
});

Test Doubles

Mocks vs Stubs vs Spies vs Fakes

Mock: Verifies behavior (calls, arguments)

const mock = vi.fn();
mock('test');
expect(mock).toHaveBeenCalledWith('test');

Stub: Returns predefined values

const stub = vi.fn().mockReturnValue(42);
expect(stub()).toBe(42);

Spy: Observes real function

const spy = vi.spyOn(obj, 'method');
obj.method();
expect(spy).toHaveBeenCalled();

Fake: Working implementation (simplified)

class FakeDatabase {
  private data = new Map();

  async save(key, value) {
    this.data.set(key, value);
  }

  async get(key) {
    return this.data.get(key);
  }
}

Coverage Analysis

Running Coverage

# Vitest
vitest --coverage

# Jest
jest --coverage

Coverage Thresholds

// vitest.config.ts
export default {
  test: {
    coverage: {
      provider: 'v8',
      reporter: ['text', 'html', 'lcov'],
      lines: 80,
      functions: 80,
      branches: 80,
      statements: 80
    }
  }
};

Coverage Best Practices

✅ DO:

  • Aim for 80-90% coverage
  • Focus on business logic
  • Test edge cases
  • Test error paths

❌ DON'T:

  • Chase 100% coverage
  • Test getters/setters only
  • Test framework code
  • Write tests just for coverage

Snapshot Testing

When to Use Snapshots

Good use cases:

  • UI component output
  • API responses
  • Configuration objects
  • Error messages
it('should render user card', () => {
  const card = renderUserCard({ name: 'John', role: 'Admin' });
  expect(card).toMatchSnapshot();
});

// Update snapshots: vitest -u

Avoid snapshots for:

  • Dates/timestamps
  • Random values
  • Large objects (prefer specific assertions)

Test Organization

File Structure

src/
├── services/
│   ├── UserService.ts
│   └── UserService.test.ts      ← Co-located
tests/
├── unit/
│   └── utils.test.ts
├── integration/
│   └── api.test.ts
└── fixtures/
    └── users.json

Test Naming

✅ GOOD:

describe('UserService.create', () => {
  it('should create user with valid email', () => {});
  it('should throw error for invalid email', () => {});
  it('should generate unique ID', () => {});
});

❌ BAD:

describe('UserService', () => {
  it('test1', () => {});
  it('should work', () => {});
});

Error Handling Tests

// Synchronous errors
it('should throw for negative numbers', () => {
  expect(() => sqrt(-1)).toThrow('Cannot compute square root of negative');
});

// Async errors
it('should reject for invalid ID', async () => {
  await expect(fetchUser('invalid')).rejects.toThrow('Invalid ID');
});

// Error types
it('should throw TypeError', () => {
  expect(() => doSomething()).toThrow(TypeError);
});

// Custom errors
it('should throw ValidationError', () => {
  expect(() => validate()).toThrow(ValidationError);
});

Test Isolation

Reset State Between Tests

let service: UserService;

beforeEach(() => {
  service = new UserService();
  vi.clearAllMocks();
});

afterEach(() => {
  vi.restoreAllMocks();
});

Avoid Test Interdependence

❌ BAD:

let user;

it('should create user', () => {
  user = createUser(); // Shared state
});

it('should update user', () => {
  updateUser(user); // Depends on previous test
});

✅ GOOD:

it('should update user', () => {
  const user = createUser();
  updateUser(user);
  expect(user.updated).toBe(true);
});

VSCode Debug Mode & Child Process Testing

CRITICAL: When tests spawn child processes (like CLI tools, hooks, or external commands), they may fail in VSCode debug mode due to NODE_OPTIONS inheritance.

Problem: NODE_OPTIONS Breaks Child Processes

VSCode debugger sets NODE_OPTIONS=--inspect-brk=<port> which child processes inherit, causing them to try to attach to the same debugger port and fail with exit code 1.

Symptoms:

  • Tests pass with "Run Test" but fail with "Debug Test"
  • Spawned processes exit with code 1 and empty output
  • spawnSync/execFileSync calls fail silently

Solution: getCleanEnv() Pattern

// src/utils/clean-env.ts
export function getCleanEnv(): NodeJS.ProcessEnv {
  const cleanEnv = { ...process.env };

  // Debugger flags (VSCode, WebStorm, IntelliJ)
  delete cleanEnv.NODE_OPTIONS;
  delete cleanEnv.NODE_INSPECT;
  delete cleanEnv.NODE_INSPECT_RESUME_ON_START;

  // Coverage/instrumentation (CI/CD pipelines)
  delete cleanEnv.NODE_V8_COVERAGE;
  delete cleanEnv.VSCODE_INSPECTOR_OPTIONS;

  return cleanEnv;
}

Usage in Tests

import { getCleanEnv } from '../test-utils/clean-env.js';
import { execSync, spawnSync } from 'child_process';

it('should execute CLI command', () => {
  const result = execSync('node my-cli.js', {
    encoding: 'utf-8',
    env: getCleanEnv(),  // ← CRITICAL for debug mode + CI/CD
  });
  expect(result).toContain('expected output');
});

it('should spawn child process', () => {
  const result = spawnSync('npm', ['run', 'build'], {
    encoding: 'utf-8',
    env: getCleanEnv(),  // ← CRITICAL
  });
  expect(result.status).toBe(0);
});

When to Use getCleanEnv

✅ ALWAYS USE when spawning:

  • CLI tools (node, npm, npx, claude, etc.)
  • External commands (git, gh, etc.)
  • Test hooks that spawn processes
  • Integration tests with real processes

❌ NOT NEEDED for:

  • Pure unit tests (no child processes)
  • Mocked dependencies
  • In-process testing

ESM Module Mocking with vi.hoisted()

CRITICAL: In ESM (ECMAScript Modules), imports are hoisted. Use vi.hoisted() to ensure mocks are defined before imports.

Problem: Mock Defined After Import

// ❌ WRONG: vi.mock hoisted, but mockFn not defined yet
vi.mock('./module', () => ({
  myFunc: mockFn  // ReferenceError: mockFn is not defined
}));

const mockFn = vi.fn();
import { myFunc } from './module';

Solution: vi.hoisted() Pattern

import { describe, it, expect, vi } from 'vitest';

// ✅ Define mocks in hoisted context FIRST
const { mockFn } = vi.hoisted(() => ({
  mockFn: vi.fn()
}));

// Now vi.mock can use the hoisted mock
vi.mock('./module', () => ({
  myFunc: mockFn
}));

// Import AFTER mock setup
import { myFunc } from './module';

describe('Module', () => {
  it('should use mocked function', () => {
    mockFn.mockReturnValue('mocked');
    expect(myFunc()).toBe('mocked');
    expect(mockFn).toHaveBeenCalled();
  });
});

Complete ESM Mocking Example

import { describe, it, expect, vi, beforeEach } from 'vitest';

// 1. Hoisted mock definitions
const { mockReadFile, mockWriteFile } = vi.hoisted(() => ({
  mockReadFile: vi.fn(),
  mockWriteFile: vi.fn()
}));

// 2. Mock the module using hoisted mocks
vi.mock('fs/promises', () => ({
  readFile: mockReadFile,
  writeFile: mockWriteFile
}));

// 3. Import AFTER mock setup (imports are automatically hoisted in Vitest)
import { readFile, writeFile } from 'fs/promises';

describe('FileService', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('should read file', async () => {
    mockReadFile.mockResolvedValue('file content');

    const content = await readFile('/path/to/file', 'utf-8');

    expect(content).toBe('file content');
    expect(mockReadFile).toHaveBeenCalledWith('/path/to/file', 'utf-8');
  });
});

vi.hoisted() vs Traditional Mocking

ApproachESM CompatibleJest Compatible
vi.hoisted() + vi.mock()✅ Yes❌ No (Vitest only)
jest.mock() with factory⚠️ Partial✅ Yes
Manual module replacement✅ Yes✅ Yes

Testing with Isolated Temp Directories

CRITICAL: Integration tests should NEVER operate on project directories. Always use isolated temp directories.

Problem: Tests Affecting Project State

// ❌ DANGEROUS: Can corrupt project state
const testDir = path.join(process.cwd(), '.specweave/test');

Solution: Isolated Temp Directories

import * as os from 'os';
import * as path from 'path';
import * as fs from 'fs/promises';

// ✅ SAFE: Unique temp directory per test run
const TEST_ROOT = path.join(
  os.tmpdir(),
  `my-test-${Date.now()}-${Math.random().toString(36).slice(2)}`
);

describe('Integration Test', () => {
  beforeEach(async () => {
    await fs.mkdir(TEST_ROOT, { recursive: true });
  });

  afterEach(async () => {
    await fs.rm(TEST_ROOT, { recursive: true, force: true });
  });

  it('should work in isolated directory', async () => {
    const testFile = path.join(TEST_ROOT, 'test.json');
    await fs.writeFile(testFile, '{"test": true}');
    // Test logic...
  });
});

CWD Restoration Pattern

When tests change working directory, always restore it to prevent affecting other tests:

describe('Tests that change CWD', () => {
  let originalCwd: string;

  beforeEach(() => {
    originalCwd = process.cwd();  // ← Save BEFORE changing
    process.chdir(TEST_ROOT);
  });

  afterEach(() => {
    process.chdir(originalCwd);   // ← Restore BEFORE cleanup
    // Now safe to delete TEST_ROOT
  });
});

Best Practices Summary

✅ DO:

  • Write tests before code (TDD)
  • Test behavior, not implementation
  • One assertion per test (when possible)
  • Clear test names (should...)
  • Mock external dependencies
  • Test edge cases and errors
  • Keep tests fast (<100ms each)
  • Use descriptive variable names
  • Clean up after tests

❌ DON'T:

  • Test private methods directly
  • Share state between tests
  • Use real databases/APIs
  • Test framework code
  • Write fragile tests (implementation-dependent)
  • Skip error cases
  • Use magic numbers
  • Leave commented-out tests

Quick Reference

Assertions

expect(value).toBe(expected);              // ===
expect(value).toEqual(expected);           // Deep equality
expect(value).toBeTruthy();                // Boolean true
expect(value).toBeFalsy();                 // Boolean false
expect(array).toHaveLength(3);             // Array length
expect(array).toContain(item);             // Array includes
expect(string).toMatch(/pattern/);         // Regex match
expect(fn).toThrow(Error);                 // Throws error
expect(obj).toHaveProperty('key');         // Has property
expect(value).toBeCloseTo(0.3, 5);        // Float comparison

Lifecycle Hooks

beforeAll(() => {});      // Once before all tests
beforeEach(() => {});     // Before each test
afterEach(() => {});      // After each test
afterAll(() => {});       // Once after all tests

Mock Utilities

vi.fn()                           // Create mock
vi.fn().mockReturnValue(x)        // Return value
vi.fn().mockResolvedValue(x)      // Async return
vi.fn().mockRejectedValue(e)      // Async error
vi.mock('./module')               // Mock module
vi.spyOn(obj, 'method')           // Spy on method
vi.clearAllMocks()                // Clear call history
vi.resetAllMocks()                // Reset + clear
vi.restoreAllMocks()              // Restore originals

This skill is self-contained and works in ANY user project with Vitest/Jest.

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

04

需要参考平台分布和安装热度时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Claude Code

30.44%
按下载量换算48

Antigravity

25.34%
按下载量换算40

Gemini CLI

18.72%
按下载量换算29

OpenCode

12.88%
按下载量换算20

Cursor

7.79%
按下载量换算12

Codex

3.75%
按下载量换算6

安全审计

暂无安全审计结果可展示。

权限和风险

执行命令

安装流程涉及命令执行,可能通过 npx skills add https://github.com/anton-abyzov/specweave --skill unit-testing-expert;npx skills add anton-abyzov/specweave --skill "unit-testing-expert" 联网下载 Skill 或依赖。用户安装前应确认命令来源、仓库内容和执行环境。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。

来源信息

继续浏览同类 Skills