Token导航 LogoToken导航

Agent Skill · 研究检索

testing-strategy(测试策略)

用于辅助测试设计、自动化测试、用例整理和回归验证。它适合让 Agent 编写单元测试、端到端测试、测试计划或根据失败日志定位问题。使用时需要确认项目测试框架、运行命令和夹具数据,避免为了通过测试而改坏真实逻辑;涉及浏览器或外部服务时,应区分本地模拟、测试环境和生产环境。

testing-strategy 是研究检索类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为操作浏览器;许可证信息还不明确,第三方扫描已通过,按安装量在研究检索类的 73,675 个 Skill 里排第 39,473。

总安装 1,409周安装 57GitHub Stars 6下载 442核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

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

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

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/mgd34msu/goodvibes-plugin --skill testing-strategy

这个 Skill 是做什么的

研究检索类 Skill 主要解决「找资料、核对事实、把来源整理清楚」的问题,通常会让助手先检索再总结。

testing-strategy 辅助制定单元、集成与端到端测试计划,提升代码质量。适用于编写测试用例、分析失败日志或优化覆盖率的目标场景。推荐高覆盖率标准(新代码100%,总体80%+),集成 CI 与 MSW 模拟。需防止片状测试,通过模拟与隔离保障确定性结果。建议按业务逻辑组织测试文件,并与源代码保持同构。

来源仓库内路径:skills/testing-strategy

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/mgd34msu/goodvibes-plugin --skill testing-stra…;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要操作浏览器,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

testing-strategy 怎么安装?

在终端执行 npx skills add https://github.com/mgd34msu/goodvibes-plugin --skill testing-strategy。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

testing-strategy 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

testing-strategy 需要什么权限?

权限级别是操作浏览器。该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

testing-strategy 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

testing-strategy 用的人多吗?

公开数据是累计安装 1,409 次,近一周安装 57 次,下载 442 次,GitHub 6 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

testing-strategy 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理研究检索类任务、又不想每次都手写提示词的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

研究检索类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
find-skills

寻找技能 · Codex、Claude

31,199,81316,486操作浏览器MIT有提醒
self-improving-agent

自我改进 Agent · OpenClaw

16,464,0023,396接触敏感数据MIT-0通过
soultrace

灵魂追踪 · Codex、Claude

7,588,94412需要联网未明确有提醒
self-improving

自我完善 · OpenClaw

6,829,2841,046需要联网MIT-0通过
microsoft-foundry

微软代工厂 · Codex、Claude

6,614,144764调用外部服务未明确有提醒
testing-strategy

测试策略 · 本页

1,4096操作浏览器未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Resources

scripts/
  validate-tests.sh
references/
  testing-patterns.md

Testing Strategy

This skill guides you through implementing comprehensive testing strategies using modern testing frameworks and GoodVibes precision tools. Use this workflow when adding tests to existing code, setting up test infrastructure, or achieving coverage goals.

When to Use This Skill

  • Setting up test infrastructure (Vitest, Jest, Playwright)
  • Writing unit tests for functions, hooks, and utilities
  • Creating component tests with React Testing Library
  • Implementing E2E tests with Playwright
  • Adding API mocking with MSW
  • Improving test coverage
  • Configuring CI test pipelines
  • Debugging flaky tests
  • Creating test fixtures and factories

Test Organization

File Naming Conventions

Follow consistent naming patterns:

// Co-located pattern (recommended)
src/
  components/
    Button.tsx
    Button.test.tsx          // Component tests
  utils/
    formatDate.ts
    formatDate.test.ts       // Unit tests
  api/
    users.ts
    users.integration.test.ts // Integration tests

// Centralized pattern (alternative)
__tests__/
  components/
    Button.test.tsx
  utils/
    formatDate.test.ts

Test Suite Structure

Organize tests with clear describe blocks:

import { describe, it, expect, beforeEach } from 'vitest';
import { formatDate } from './formatDate';

describe('formatDate', () => {
  describe('with valid dates', () => {
    it('formats ISO dates to MM/DD/YYYY', () => {
      expect(formatDate('2024-01-15')).toBe('01/15/2024');
    });

    it('handles Date objects', () => {
      const date = new Date('2024-01-15');
      expect(formatDate(date)).toBe('01/15/2024');
    });
  });

  describe('with invalid dates', () => {
    it('throws for invalid strings', () => {
      expect(() => formatDate('invalid')).toThrow('Invalid date');
    });

    it('throws for null', () => {
      expect(() => formatDate(null)).toThrow('Invalid date');
    });
  });
});

Discovery: Finding Test Files

Use precision tools to discover existing test patterns:

# Find all test files and analyze patterns
discover:
  queries:
    - id: test-files
      type: glob
      patterns:
        - "**/*.test.{ts,tsx,js,jsx}"
        - "**/*.spec.{ts,tsx,js,jsx}"
        - "e2e/**/*.spec.ts"
      exclude:
        - "**/node_modules/**"
        - "**/dist/**"
    - id: test-config
      type: glob
      patterns:
        - "vitest.config.ts"
        - "jest.config.js"
        - "playwright.config.ts"
    - id: skipped-tests
      type: grep
      pattern: "\\.skip|it\\.only|describe\\.only"
      glob: "**/*.test.{ts,tsx}"
  output_mode: files_only

Unit Testing

Vitest Setup (Recommended)

// vitest.config.ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: ['./src/test/setup.ts'],
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html'],
      exclude: [
        'node_modules/',
        'src/test/',
        '**/*.d.ts',
        '**/*.config.*',
        '**/dist/**',
      ],
      all: true,
      lines: 80,
      functions: 80,
      branches: 80,
      statements: 80,
    },
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
    },
  },
});

Testing Pure Functions

import { describe, it, expect } from 'vitest';
import { calculateTotal, discountPrice } from './pricing';

describe('pricing utilities', () => {
  describe('calculateTotal', () => {
    it('sums item prices', () => {
      const items = [
        { price: 10.00, quantity: 2 },
        { price: 5.50, quantity: 1 },
      ];
      expect(calculateTotal(items)).toBe(25.50);
    });

    it('returns 0 for empty array', () => {
      expect(calculateTotal([])).toBe(0);
    });

    it('handles quantity multipliers', () => {
      const items = [{ price: 10, quantity: 3 }];
      expect(calculateTotal(items)).toBe(30);
    });
  });

  describe('discountPrice', () => {
    it('applies percentage discount', () => {
      expect(discountPrice(100, 0.2)).toBe(80);
    });

    it('rounds to 2 decimal places', () => {
      expect(discountPrice(10.99, 0.15)).toBe(9.34);
    });

    it('throws for invalid discounts', () => {
      expect(() => discountPrice(100, -0.1)).toThrow('Invalid discount');
      expect(() => discountPrice(100, 1.5)).toThrow('Invalid discount');
    });
  });
});

Testing React Hooks

import { renderHook, waitFor } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { useAuth } from './useAuth';

// Mock the auth context
vi.mock('@/contexts/AuthContext', () => ({
  useAuthContext: vi.fn(),
}));

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

  it('returns user when authenticated', () => {
    vi.mocked(useAuthContext).mockReturnValue({
      user: { id: '1', email: 'test@example.com' },
      isLoading: false,
    });

    const { result } = renderHook(() => useAuth());

    expect(result.current.user).toEqual({
      id: '1',
      email: 'test@example.com',
    });
    expect(result.current.isAuthenticated).toBe(true);
  });

  it('handles loading state', () => {
    vi.mocked(useAuthContext).mockReturnValue({
      user: null,
      isLoading: true,
    });

    const { result } = renderHook(() => useAuth());

    expect(result.current.isLoading).toBe(true);
    expect(result.current.isAuthenticated).toBe(false);
  });

  it('refetches user on login', async () => {
    const refetch = vi.fn().mockResolvedValue({ id: '1' });
    vi.mocked(useAuthContext).mockReturnValue({
      user: null,
      refetch,
    });

    const { result } = renderHook(() => useAuth());

    await result.current.login('test@example.com', 'password');

    expect(refetch).toHaveBeenCalledOnce();
  });
});

Component Testing

React Testing Library Patterns

import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect, vi } from 'vitest';
import { Button } from './Button';
import { SearchInput } from './SearchInput';

describe('Button', () => {
  it('renders with text', () => {
    render(<Button>Click me</Button>);
    expect(screen.getByRole('button', { name: /click me/i })).toBeInTheDocument();
  });

  it('calls onClick when clicked', async () => {
    const handleClick = vi.fn();
    const user = userEvent.setup();

    render(<Button onClick={handleClick}>Click</Button>);
    await user.click(screen.getByRole('button'));

    expect(handleClick).toHaveBeenCalledOnce();
  });

  it('disables button when loading', () => {
    render(<Button isLoading>Submit</Button>);
    expect(screen.getByRole('button')).toBeDisabled();
  });

  it('shows spinner when loading', () => {
    render(<Button isLoading>Submit</Button>);
    expect(screen.getByRole('button')).toHaveAttribute('aria-busy', 'true');
  });

  it('applies variant styles', () => {
    const { container } = render(<Button variant="primary">Primary</Button>);
    expect(container.firstChild).toHaveClass('bg-blue-600');
  });
});

describe('SearchInput', () => {
  it('debounces search input', async () => {
    const onSearch = vi.fn();
    const user = userEvent.setup();

    render(<SearchInput onSearch={onSearch} debounce={300} />);
    const input = screen.getByRole('searchbox');

    await user.type(input, 'test query');

    // Should not call immediately
    expect(onSearch).not.toHaveBeenCalled();

    // Should call after debounce
    await waitFor(
      () => expect(onSearch).toHaveBeenCalledWith('test query'),
      { timeout: 500 }
    );
  });

  it('clears input on clear button click', async () => {
    const user = userEvent.setup();

    render(<SearchInput />);
    const input = screen.getByRole('searchbox') as HTMLInputElement;

    await user.type(input, 'test');
    expect(input.value).toBe('test');

    await user.click(screen.getByRole('button', { name: /clear/i }));
    expect(input.value).toBe('');
  });
});

Testing Async Components

import { render, screen, waitFor } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { UserProfile } from './UserProfile';
import * as api from '@/api/users';

vi.mock('@/api/users');

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

  it('shows loading state initially', () => {
    vi.mocked(api.getUser).mockReturnValue(new Promise(() => {}));

    render(<UserProfile userId="1" />);
    expect(screen.getByText(/loading/i)).toBeInTheDocument();
  });

  it('displays user data when loaded', async () => {
    vi.mocked(api.getUser).mockResolvedValue({
      id: '1',
      name: 'John Doe',
      email: 'john@example.com',
    });

    render(<UserProfile userId="1" />);

    await waitFor(() => {
      expect(screen.getByText('John Doe')).toBeInTheDocument();
    });

    expect(screen.getByText('john@example.com')).toBeInTheDocument();
  });

  it('shows error message on fetch failure', async () => {
    vi.mocked(api.getUser).mockRejectedValue(new Error('Failed to fetch'));

    render(<UserProfile userId="1" />);

    await waitFor(() => {
      expect(screen.getByText(/failed to load/i)).toBeInTheDocument();
    });
  });
});

Integration Testing

API Route Testing

import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { createMocks } from 'node-mocks-http';
import { POST } from '@/app/api/posts/route';
import { prisma } from '@/lib/prisma';
import { createTestUser, cleanupDatabase } from '@/test/helpers';

describe('POST /api/posts', () => {
  let testUser: { id: string; email: string };

  beforeAll(async () => {
    testUser = await createTestUser();
  });

  afterAll(async () => {
    await cleanupDatabase();
  });

  it('creates a new post', async () => {
    const { req } = createMocks({
      method: 'POST',
      body: {
        title: 'Test Post',
        content: 'Test content',
      },
      headers: {
        authorization: `Bearer ${testUser.token}`,
      },
    });

    const response = await POST(req);
    const data = await response.json();

    expect(response.status).toBe(201);
    expect(data).toMatchObject({
      title: 'Test Post',
      content: 'Test content',
      authorId: testUser.id,
    });

    // Verify in database
    const post = await prisma.post.findUnique({
      where: { id: data.id },
    });
    expect(post).toBeTruthy();
  });

  it('validates required fields', async () => {
    const { req } = createMocks({
      method: 'POST',
      body: { title: '' }, // Missing content
      headers: { authorization: `Bearer ${testUser.token}` },
    });

    const response = await POST(req);
    const data = await response.json();

    expect(response.status).toBe(400);
    expect(data.error).toMatchObject({
      fieldErrors: {
        title: expect.arrayContaining([expect.any(String)]),
        content: expect.arrayContaining([expect.any(String)]),
      },
    });
  });

  it('requires authentication', async () => {
    const { req } = createMocks({
      method: 'POST',
      body: { title: 'Test', content: 'Test' },
      // No authorization header
    });

    const response = await POST(req);
    expect(response.status).toBe(401);
  });
});

Database Testing with Fixtures

// test/helpers.ts
import { PrismaClient } from '@prisma/client';
import { hash } from 'bcrypt';

export const prisma = new PrismaClient();

export async function createTestUser(overrides = {}) {
  const hashedPassword = await hash('password123', 10);

  return prisma.user.create({
    data: {
      email: `test-${Date.now()}@example.com`,
      name: 'Test User',
      password: hashedPassword,
      ...overrides,
    },
  });
}

export async function createTestPost(userId: string, overrides = {}) {
  return prisma.post.create({
    data: {
      title: 'Test Post',
      content: 'Test content',
      authorId: userId,
      published: false,
      ...overrides,
    },
  });
}

export async function cleanupDatabase() {
  // Delete in correct order to respect foreign keys
  await prisma.comment.deleteMany();
  await prisma.post.deleteMany();
  await prisma.user.deleteMany();
}

E2E Testing

Playwright Setup

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './e2e',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: 'html',
  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
  },
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
  ],
  webServer: {
    command: 'npm run dev',
    url: 'http://localhost:3000',
    reuseExistingServer: !process.env.CI,
  },
});

Page Object Pattern

// e2e/pages/LoginPage.ts
import { Page, Locator } from '@playwright/test';

export class LoginPage {
  readonly page: Page;
  readonly emailInput: Locator;
  readonly passwordInput: Locator;
  readonly submitButton: Locator;
  readonly errorMessage: Locator;

  constructor(page: Page) {
    this.page = page;
    this.emailInput = page.getByLabel('Email');
    this.passwordInput = page.getByLabel('Password');
    this.submitButton = page.getByRole('button', { name: 'Sign in' });
    this.errorMessage = page.getByRole('alert');
  }

  async goto() {
    await this.page.goto('/login');
  }

  async login(email: string, password: string) {
    await this.emailInput.fill(email);
    await this.passwordInput.fill(password);
    await this.submitButton.click();
  }

  async expectError(message: string) {
    // Note: expect is imported from @playwright/test
    await expect(this.errorMessage).toContainText(message);
  }
}

// e2e/auth.spec.ts
import { test, expect } from '@playwright/test';
import { LoginPage } from './pages/LoginPage';

test.describe('Authentication', () => {
  test('successful login redirects to dashboard', async ({ page }) => {
    const loginPage = new LoginPage(page);
    await loginPage.goto();
    await loginPage.login('test@example.com', 'password123');

    await expect(page).toHaveURL('/dashboard');
    await expect(page.getByText('Welcome back')).toBeVisible();
  });

  test('invalid credentials show error', async ({ page }) => {
    const loginPage = new LoginPage(page);
    await loginPage.goto();
    await loginPage.login('invalid@example.com', 'wrong');

    await loginPage.expectError('Invalid email or password');
  });

  test('preserves redirect after login', async ({ page }) => {
    await page.goto('/dashboard');
    await expect(page).toHaveURL(/\/login\?redirect=%2Fdashboard/);

    const loginPage = new LoginPage(page);
    await loginPage.login('test@example.com', 'password123');

    await expect(page).toHaveURL('/dashboard');
  });
});

Mocking Patterns

MSW for API Mocking

// src/test/mocks/handlers.ts
import { http, HttpResponse } from 'msw';

export const handlers = [
  http.get('/api/user', () => {
    return HttpResponse.json({
      id: '1',
      email: 'test@example.com',
      name: 'Test User',
    });
  }),

  http.post('/api/posts', async ({ request }) => {
    const body = await request.json();
    return HttpResponse.json(
      {
        id: '1',
        ...body,
        createdAt: new Date().toISOString(),
      },
      { status: 201 }
    );
  }),

  http.get('/api/posts/:id', ({ params }) => {
    const { id } = params;

    if (id === '404') {
      return new HttpResponse(null, { status: 404 });
    }

    return HttpResponse.json({
      id,
      title: 'Test Post',
      content: 'Test content',
    });
  }),
];

// src/test/setup.ts
import { beforeAll, afterEach, afterAll } from 'vitest';
import { setupServer } from 'msw/node';
import { handlers } from './mocks/handlers';

const server = setupServer(...handlers);

beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

Module Mocking

import { vi } from 'vitest';
import type * as PrismaModule from '@/lib/prisma';

// Mock entire module
vi.mock('@/lib/prisma', () => ({
  prisma: {
    user: {
      findUnique: vi.fn(),
      create: vi.fn(),
      update: vi.fn(),
      delete: vi.fn(),
    },
    post: {
      findMany: vi.fn(),
      create: vi.fn(),
    },
  },
}));

// Mock with implementation
vi.mock('@/lib/auth', () => ({
  hashPassword: vi.fn((password: string) => `hashed_${password}`),
  verifyPassword: vi.fn((password: string, hash: string) => {
    return hash === `hashed_${password}`;
  }),
}));

// Partial mock
vi.mock('@/lib/email', async (importOriginal) => {
  const actual = await importOriginal<typeof import('@/lib/email')>();
  return {
    ...actual,
    sendEmail: vi.fn(), // Mock only sendEmail
  };
});

Coverage Strategy

Target Thresholds

Aim for high coverage on new code:

  • 100% on new code (enforced in CI)
  • 80%+ on existing code (gradual improvement)
  • 100% on critical paths (auth, payment, data integrity)

Coverage Configuration

// vitest.config.ts
export default defineConfig({
  test: {
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json-summary', 'html'],
      exclude: [
        'node_modules/',
        'src/test/',
        '**/*.d.ts',
        '**/*.config.*',
        '**/dist/**',
        '**/*.stories.tsx', // Storybook
        '**/types/**',
      ],
      all: true,
      lines: 80,
      functions: 80,
      branches: 80,
      statements: 80,
      // Fail CI if coverage drops
      thresholds: {
        lines: 80,
        functions: 80,
        branches: 80,
        statements: 80,
      },
    },
  },
});

Identifying Coverage Gaps

Use precision tools to find untested code:

# Find files missing tests
precision_exec:
  commands:
    - cmd: "npm run test:coverage -- --reporter=json-summary"
      expect:
        exit_code: 0

# Parse coverage report
precision_read:
  files:
    - path: "coverage/coverage-summary.json"
  extract: content
  verbosity: minimal

# Find source files without corresponding tests
discover:
  queries:
    - id: source-files
      type: glob
      patterns: ["src/**/*.{ts,tsx}"]
      exclude: ["**/*.test.*", "**/*.spec.*"]
    - id: test-files
      type: glob
      patterns: ["**/*.test.{ts,tsx}"]
  output_mode: files_only

CI Integration

GitHub Actions Example

# .github/workflows/test.yml
name: Tests

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - run: npm ci

      - name: Run unit tests
        run: npm run test:coverage

      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          files: ./coverage/coverage-final.json

      - name: Run E2E tests
        run: npx playwright test

      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 30

Parallel Test Execution

// vitest.config.ts - parallel by default
export default defineConfig({
  test: {
    pool: 'threads',
    poolOptions: {
      threads: {
        singleThread: false,
        maxThreads: 8,
      },
    },
  },
});

// playwright.config.ts
export default defineConfig({
  workers: process.env.CI ? 1 : 4, // Limit parallelism in CI
  fullyParallel: true,
});

Precision Tool Integration

Running Tests with Expectations

# Run tests and validate output
precision_exec:
  commands:
    - cmd: "npm run test -- --run"
      expect:
        exit_code: 0

    - cmd: "npm run typecheck"
      expect:
        exit_code: 0

    - cmd: "npm run test:coverage -- --run"
      expect:
        exit_code: 0
        stdout_contains: "All files"

  verbosity: minimal

Batch Test Validation

batch:
  id: validate-tests
  operations:
    query:
      - id: find-skipped
        type: grep
        pattern: "\\.skip|it\\.only|describe\\.only"
        glob: "**/*.test.ts"
        output:
          format: count_only

    exec:
      - id: run-tests
        type: command
        commands:
          - cmd: "npm run test -- --run"
            expect: { exit_code: 0 }

      - id: check-coverage
        type: command
        commands:
          - cmd: "npm run test:coverage -- --run"
            expect:
              exit_code: 0
              stdout_contains: "All files"

  config:
    execution:
      mode: sequential

Debugging Flaky Tests

Common Causes

  1. Race conditions: Use waitFor for async operations
  2. Time-dependent tests: Mock timers with vi.useFakeTimers()
  3. Test isolation: Ensure tests don't share state
  4. Network requests: Mock with MSW, don't rely on real APIs
  5. Random data: Use deterministic test data or seed random generators

Flaky Test Patterns

import { vi, beforeEach, afterEach } from 'vitest';

// Mock timers for time-dependent code
beforeEach(() => {
  vi.useFakeTimers();
});

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

it('debounces function calls', async () => {
  const callback = vi.fn();
  const debounced = debounce(callback, 1000);

  debounced('test');
  expect(callback).not.toHaveBeenCalled();

  vi.advanceTimersByTime(1000);
  expect(callback).toHaveBeenCalledWith('test');
});

// Ensure test isolation
beforeEach(async () => {
  await cleanupDatabase();
  vi.clearAllMocks();
});

// Use deterministic data
it('sorts users by creation date', () => {
  const users = [
    { id: '1', createdAt: new Date('2024-01-01') },
    { id: '2', createdAt: new Date('2024-01-02') },
  ];

  const sorted = sortByDate(users);
  expect(sorted[0].id).toBe('2');
});

Best Practices

  1. Write tests first (TDD) for new features
  2. Test behavior, not implementation - focus on what, not how
  3. One assertion per test when possible for clarity
  4. Use descriptive test names - "it does X when Y"
  5. Avoid mocking everything - integration tests need real collaborators
  6. Keep tests fast - unit tests <100ms, integration <1s
  7. Test edge cases - null, undefined, empty arrays, boundary values
  8. Don't test framework code - test your logic, not React/Vue/etc.
  9. Maintain test fixtures - keep test data realistic and up-to-date
  10. Review test coverage - 100% coverage doesn't mean bug-free

Common Anti-Patterns

[X] Testing Implementation Details

// BAD - tests internal state
it('sets loading to true', () => {
  const { result } = renderHook(() => useUsers());
  expect(result.current.loading).toBe(true);
});

// GOOD - tests user-visible behavior
it('shows loading spinner while fetching', () => {
  render(<UserList />);
  expect(screen.getByRole('status')).toBeInTheDocument();
});

[X] Overmocking

// BAD - mocks everything, tests nothing
vi.mock('./api');
vi.mock('./utils');
vi.mock('./hooks');

// GOOD - mock only external dependencies
vi.mock('axios'); // Mock HTTP client
// Let your code run for real

[X] Brittle Selectors

// BAD - breaks when styling changes
const button = container.querySelector('.btn-primary');

// GOOD - uses accessible queries
const button = screen.getByRole('button', { name: /submit/i });

Summary

Effective testing requires:

  • Organized test files co-located with source code
  • Comprehensive unit tests for business logic and utilities
  • User-centric component tests with React Testing Library
  • Realistic integration tests for API routes and database operations
  • End-to-end tests for critical user flows
  • API mocking with MSW for deterministic tests
  • High coverage targets (100% on new code, 80%+ overall)
  • CI integration with parallel execution and coverage reporting
  • Flaky test prevention through proper mocking and isolation

Use precision tools to discover test gaps, run tests efficiently, and validate implementation quality.

平台分布

Codex

38.19%
按下载量换算169

Claude

27.06%
按下载量换算120

Cursor

19.4%
按下载量换算86

Gemini CLI

9.16%
按下载量换算40

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

操作浏览器

该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills