mock-generation

SKILL.md

Mock生成技能

这个技能专门负责生成测试所需的Mock数据和Stub函数,帮助隔离测试环境并提高测试效率。

技能能力

1. 依赖分析

  • 自动识别需要Mock的外部依赖
  • 分析函数签名和返回值类型
  • 检测数据库操作、API调用等外部交互
  • 识别时间和随机等不可控因素

2. Mock数据生成

  • 生成符合类型定义的Mock对象
  • 创建真实的测试数据集
  • 支持复杂嵌套对象的生成
  • 生成边界值和异常数据

3. Stub函数创建

  • 创建可控的函数替代品
  • 支持多种返回值模式
  • 记录调用历史和参数
  • 模拟异步操作

使用方式

基础Mock生成

// 为模块生成Mock
await generateMock('src/api/userService', {
  framework: 'vitest',
  includeReturnValues: true
});

// 为特定函数生成Mock
await generateFunctionMock('fetchUserData', {
  returnType: 'User',
  async: true,
  errorScenarios: true
});

高级选项

await generateMock(target, {
  framework: 'vitest',
  mockType: 'auto', // 'auto' | 'partial' | 'full'
  includeSpies: true,
  generateTestData: true,
  customReturnValues: {
    'getUser': () => ({ id: 1, name: 'Test User' }),
    'createUser': () => Promise.resolve({ success: true })
  }
});

Mock模板

Jest模板

// Mock整个模块
jest.mock('{{modulePath}}', () => ({
  {{#each exports}}
  {{name}}: jest.fn(),
  {{/each}}
}));

// Mock实现
{{#each exports}}
{{name}}.mockImplementation(({{
  #if isAsync}}
  {{#if hasParams}}
  async ({ {{join params ", "}} }) => {
    // Mock implementation
    return {{defaultValue}};
  }
  {{else}}
  async () => {
    return {{defaultValue}};
  }
  {{/if}}
  {{else}}
  {{#if hasParams}}
  ({ {{join params ", "}} }) => {
    // Mock implementation
    return {{defaultValue}};
  }
  {{else}}
  () => {
    return {{defaultValue}};
  }
  {{/if}}
  {{/if}}
}));
{{/each}}

// 在测试中使用
beforeEach(() => {
  {{#each exports}}
  {{name}}.mockClear();
  {{/each}}
});

Vitest模板

import { vi } from 'vitest';

// Mock整个模块
vi.mock('{{modulePath}}', () => ({
  {{#each exports}}
  {{name}}: vi.fn(),
  {{/each}}
}));

// Mock实现
{{#each exports}}
export const {{name}} = vi.fn();
{{#if isAsync}}
{{name}}.mockResolvedValue({{defaultValue}});
{{else}}
{{name}}.mockReturnValue({{defaultValue}});
{{/if}}
{{/each}}

Python Mock模板

from unittest.mock import Mock, patch
import pytest

{{#each functions}}
# Mock decorator
@patch('{{modulePath}}.{{name}}')
def test_{{testName}}(mock_{{name}}):
    # Setup mock return value
    mock_{{name}}.return_value = {{defaultValue}}

    # Test implementation
    result = function_under_test()

    # Assertions
    assert result is not None
    {{#if verifyCalled}}
    mock_{{name}}.assert_called_once()
    {{/if}}
{{/each}}

数据生成器

基础类型生成器

class DataGenerator {
  static generateString(options?: {
    length?: number;
    pattern?: string;
    enum?: string[];
  }): string {
    if (options?.enum) {
      return options.enum[0];
    }

    if (options?.pattern) {
      return this.matchPattern(options.pattern);
    }

    const length = options?.length || 10;
    return this.randomString(length);
  }

  static generateNumber(options?: {
    min?: number;
    max?: number;
    integer?: boolean;
    enum?: number[];
  }): number {
    if (options?.enum) {
      return options.enum[0];
    }

    const min = options?.min || 0;
    const max = options?.max || 100;

    if (options?.integer) {
      return Math.floor(Math.random() * (max - min + 1)) + min;
    }

    return Math.random() * (max - min) + min;
  }

  static generateArray<T>(itemGenerator: () => T, options?: {
    length?: number;
    minLength?: number;
    maxLength?: number;
  }): T[] {
    const length = options?.length ||
      Math.floor(Math.random() * (options?.maxLength || 5)) + (options?.minLength || 0);

    return Array.from({ length }, itemGenerator);
  }

  static generateObject(schema: ObjectSchema): any {
    const obj: any = {};

    for (const [key, value] of Object.entries(schema)) {
      if (typeof value === 'function') {
        obj[key] = value();
      } else if (Array.isArray(value)) {
        obj[key] = this.generateArray(() => value[0]);
      } else if (typeof value === 'object') {
        obj[key] = this.generateObject(value);
      } else {
        obj[key] = value;
      }
    }

    return obj;
  }
}

复杂对象生成

// 用户对象生成器
const UserGenerator = {
  generate: (overrides?: Partial<User>): User => ({
    id: DataGenerator.generateNumber({ integer: true, min: 1 }),
    name: DataGenerator.generateString({ length: 10 }),
    email: DataGenerator.generateEmail(),
    age: DataGenerator.generateNumber({ min: 18, max: 65, integer: true }),
    isActive: true,
    createdAt: new Date().toISOString(),
    ...overrides
  }),

  generateList: (count: number = 3): User[] =>
    DataGenerator.generateArray(() => UserGenerator.generate(), { length: count }),

  generateWithInvalidData: (): User => ({
    id: -1,
    name: '',
    email: 'invalid-email',
    age: -1,
    isActive: false,
    createdAt: 'invalid-date'
  })
};

API Mock生成

REST API Mock

// API响应生成器
class APIResponseGenerator {
  static generateSuccessResponse<T>(data: T): APIResponse<T> {
    return {
      success: true,
      data,
      message: 'Success',
      timestamp: new Date().toISOString()
    };
  }

  static generateErrorResponse(errorCode: string, message: string): APIResponse<null> {
    return {
      success: false,
      data: null,
      error: {
        code: errorCode,
        message,
        details: {}
      },
      timestamp: new Date().toISOString()
    };
  }

  static generatePaginatedResponse<T>(
    items: T[],
    page: number = 1,
    limit: number = 10
  ): PaginatedResponse<T> {
    return {
      success: true,
      data: items,
      pagination: {
        page,
        limit,
        total: items.length,
        totalPages: Math.ceil(items.length / limit)
      }
    };
  }
}

// Fetch Mock
const mockFetch = jest.fn();
mockFetch.mockImplementation(async (url: string, options?: RequestInit) => {
  if (url.includes('/users')) {
    return {
      ok: true,
      status: 200,
      json: async () => APIResponseGenerator.generateSuccessResponse(
        UserGenerator.generateList()
      )
    };
  }

  if (url.includes('/error')) {
    return {
      ok: false,
      status: 500,
      json: async () => APIResponseGenerator.generateErrorResponse(
        'INTERNAL_ERROR',
        'Something went wrong'
      )
    };
  }

  return {
    ok: false,
    status: 404,
    json: async () => ({})
  };
});

数据库Mock

数据库操作Mock

// 数据库连接Mock
const mockDb = {
  users: {
    findById: jest.fn(),
    create: jest.fn(),
    update: jest.fn(),
    delete: jest.fn(),
    findMany: jest.fn()
  },
  transactions: {
    begin: jest.fn(),
    commit: jest.fn(),
    rollback: jest.fn()
  }
};

// 设置Mock返回值
mockDb.users.findById.mockImplementation(async (id: number) => {
  if (id === 999) {
    return null;
  }
  return UserGenerator.generate({ id });
});

mockDb.users.create.mockImplementation(async (userData: Partial<User>) => {
  return UserGenerator.generate(userData);
});

时间Mock

时间相关测试

// Jest时间Mock
beforeEach(() => {
  jest.useFakeTimers();
});

afterEach(() => {
  jest.useRealTimers();
});

// 测试中的时间控制
it('should execute callback after delay', () => {
  const callback = jest.fn();

  setTimeout(callback, 1000);

  // 快进时间
  jest.advanceTimersByTime(1000);

  expect(callback).toHaveBeenCalled();
});

// Vitest时间Mock
import { vi } from 'vitest';

beforeEach(() => {
  vi.useFakeTimers();
});

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

特殊场景Mock

错误场景

// 错误Mock生成器
class ErrorMockGenerator {
  static generateNetworkError(): Error {
    const error = new Error('Network Error');
    error.name = 'NetworkError';
    error.code = 'NETWORK_ERROR';
    return error;
  }

  static generateTimeoutError(): Error {
    const error = new Error('Request timeout');
    error.name = 'TimeoutError';
    error.code = 'TIMEOUT';
    return error;
  }

  static generateValidationError(field: string): Error {
    const error = new Error(`Validation failed for field: ${field}`);
    error.name = 'ValidationError';
    error.field = field;
    return error;
  }
}

// 使用错误Mock
mockApi.getUser.mockRejectedValue(ErrorMockGenerator.generateNetworkError());

异步操作

// 异步操作Mock
const asyncMock = jest.fn();
asyncMock
  .mockResolvedValueOnce('first call result')
  .mockRejectedValueOnce(new Error('second call error'))
  .mockResolvedValueOnce('third call result');

// Promise链测试
it('should handle promise chain', async () => {
  const result = await promiseChain();

  expect(result).toBe('final result');
  expect(asyncMock).toHaveBeenCalledTimes(3);
});

最佳实践

1. Mock范围控制

  • 只Mock外部依赖
  • 避免过度Mock
  • 保持Mock简单

2. Mock数据管理

  • 使用工厂模式生成测试数据
  • 创建可重用的数据集
  • 保持数据一致性

3. Mock验证

  • 验证Mock的调用
  • 检查调用参数
  • 确保Mock被正确使用

4. 清理和重置

  • 在每个测试后重置Mock
  • 避免测试间的干扰
  • 使用beforeEach/afterEach
Weekly Installs
6
First Seen
Jan 24, 2026
Installed on
antigravity6
claude-code6
opencode6
qoder5
gemini-cli5
windsurf4