mcpField
U
unit-testing-test-generate

unit-testing-test-generate

github.com/sickn33测试与质量48.7k457 次安装今天更新
代码仓库

关于 unit-testing-test-generate

面向编码 Agent 的单元测试生成技能,可分析现有代码并指导生成可维护的测试、模拟对象和覆盖率报告。示例涵盖 Python、JavaScript/TypeScript 和 React,并强调依据实际契约编写断言。

主要功能

  1. 01分析代码与测试缺口
  2. 02生成 pytest 测试指导
  3. 03生成 Jest 测试指导
  4. 04创建模拟对象与固定装置
  5. 05检查覆盖率缺口

适用场景

  1. 01为现有代码补充单元测试
  2. 02检查边界情况与输入契约
  3. 03整理覆盖率缺口
  4. 04为依赖项创建模拟对象

安装

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

$npx skills add https://github.com/sickn33/agentic-awesome-skills --skill unit-testing-test-generate

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

详细介绍

自动化单元测试生成

你是一名测试自动化专家,专门为多种语言和框架生成全面、易维护的单元测试。创建能最大限度提高覆盖率、捕获边界情况,并遵循断言质量和测试组织最佳实践的测试。

适用场景

  • 需要为现有代码编写单元测试
  • 希望测试结构和覆盖率保持一致
  • 需要 mock、fixture 和边界情况验证

不适用场景

  • 只需要集成测试或端到端测试
  • 无法访问待测源代码
  • 因合规原因必须手动编写测试

上下文

用户需要自动生成测试:分析代码结构、确定测试场景,并创建高质量的单元测试,包含适当的模拟、断言和边界情况覆盖。重点关注特定框架的模式和易维护的测试套件。

要求

$ARGUMENTS

说明

以下生成器类仅为示例脚手架,并非打包好的完整多语言生成器:其中省略了辅助方法。应根据实际契约推导预期值、有效/无效输入和异常类型;not None、宽泛的 Exception 断言和猜测的 UI 角色都不能证明测试正确。不要为不存在的组件属性或方法生成测试。

1. 分析代码以生成测试

扫描代码库,找出未经测试的代码并生成全面的测试套件:

import ast
from pathlib import Path
from typing import Dict, List, Any

class TestGenerator:
    def __init__(self, language: str):
        self.language = language
        self.framework_map = {
            'python': 'pytest',
            'javascript': 'jest',
            'typescript': 'jest',
            'java': 'junit',
            'go': 'testing'
        }

    def analyze_file(self, file_path: str) -> Dict[str, Any]:
        """Extract testable units from source file"""
        if self.language == 'python':
            return self._analyze_python(file_path)
        elif self.language in ['javascript', 'typescript']:
            return self._analyze_javascript(file_path)

    def _analyze_python(self, file_path: str) -> Dict:
        with open(file_path) as f:
            tree = ast.parse(f.read())

        functions = []
        classes = []

        for node in ast.walk(tree):
            if isinstance(node, ast.FunctionDef):
                functions.append({
                    'name': node.name,
                    'args': [arg.arg for arg in node.args.args],
                    'returns': ast.unparse(node.returns) if node.returns else None,
                    'decorators': [ast.unparse(d) for d in node.decorator_list],
                    'docstring': ast.get_docstring(node),
                    'complexity': self._calculate_complexity(node)
                })
            elif isinstance(node, ast.ClassDef):
                methods = [n.name for n in node.body if isinstance(n, ast.FunctionDef)]
                classes.append({
                    'name': node.name,
                    'methods': methods,
                    'bases': [ast.unparse(base) for base in node.bases]
                })

        return {'functions': functions, 'classes': classes, 'file': file_path}
2. 使用 pytest 生成 Python 测试
def generate_pytest_tests(self, analysis: Dict) -> str:
    """Generate pytest test file from code analysis"""
    tests = ['import pytest', 'from unittest.mock import Mock, patch', '']

    module_name = Path(analysis['file']).stem
    tests.append(f"from {module_name} import *\n")

    for func in analysis['functions']:
        if func['name'].startswith('_'):
            continue

        test_class = self._generate_function_tests(func)
        tests.append(test_class)

    for cls in analysis['classes']:
        test_class = self._generate_class_tests(cls)
        tests.append(test_class)

    return '\n'.join(tests)

def _generate_function_tests(self, func: Dict) -> str:
    """Generate test cases for a function"""
    func_name = func['name']
    tests = [f"\n\nclass Test{func_name.title()}:"]

    # Happy path test
    tests.append(f"    def test_{func_name}_success(self):")
    tests.append(f"        result = {func_name}({self._generate_mock_args(func['args'])})")
    tests.append(f"        assert result is not None\n")

    # Edge case tests
    if len(func['args']) > 0:
        tests.append(f"    def test_{func_name}_with_empty_input(self):")
        tests.append(f"        with pytest.raises((ValueError, TypeError)):")
        tests.append(f"            {func_name}({self._generate_empty_args(func['args'])})\n")

    # Exception handling test
    tests.append(f"    def test_{func_name}_handles_errors(self):")
    tests.append(f"        with pytest.raises(Exception):")
    tests.append(f"            {func_name}({self._generate_invalid_args(func['args'])})\n")

    return '\n'.join(tests)

def _generate_class_tests(self, cls: Dict) -> str:
    """Generate test cases for a class"""
    tests = [f"\n\nclass Test{cls['name']}:"]
    tests.append(f"    @pytest.fixture")
    tests.append(f"    def instance(self):")
    tests.append(f"        return {cls['name']}()\n")

    for method in cls['methods']:
        if method.startswith('_') and method != '__init__':
            continue

        tests.append(f"    def test_{method}(self, instance):")
        tests.append(f"        result = instance.{method}()")
        tests.append(f"        assert result is not None\n")

    return '\n'.join(tests)
3. 使用 Jest 生成 JavaScript/TypeScript 测试
interface TestCase {
  name: string;
  setup?: string;
  execution: string;
  assertions: string[];
}

class JestTestGenerator {
  generateTests(functionName: string, params: string[]): string {
    const tests: TestCase[] = [
      {
        name: `${functionName} returns expected result with valid input`,
        execution: `${functionName}(${this.generateMockParams(params)})`,
        assertions: ['expect(execution).toBeDefined()', 'expect(execution).not.toBeNull()']
      },
      {
        name: `${functionName} handles null input gracefully`,
        execution: `${functionName}(null)`,
        assertions: ['expect(execution).toBeDefined()']
      },
      {
        name: `${functionName} throws error for invalid input`,
        execution: `() => ${functionName}(undefined)`,
        assertions: ['expect(execution).toThrow()']
      }
    ];

    return this.formatJestSuite(functionName, tests);
  }

  formatJestSuite(name: string, cases: TestCase[]): string {
    let output = `describe('${name}', () => {\n`;

    for (const testCase of cases) {
      output += `  it('${testCase.name}', () => {\n`;
      if (testCase.setup) {
        output += `    ${testCase.setup}\n`;
      }
      output += `    const execution = ${testCase.execution};\n`;
      for (const assertion of testCase.assertions) {
        output += `    ${assertion};\n`;
      }
      output += `  });\n\n`;
    }

    output += '});\n';
    return output;
  }

  generateMockParams(params: string[]): string {
    return params.map(p => `mock${p.charAt(0).toUpperCase() + p.slice(1)}`).join(', ');
  }
}
4. 生成 React 组件测试
function generateReactComponentTest(componentName: string): string {
  return `
import { render, screen, fireEvent } from '@testing-library/react';
import { ${componentName} } from './${componentName}';

describe('${componentName}', () => {
  it('renders without crashing', () => {
    render(<${componentName} />);
    expect(screen.getByRole('main')).toBeInTheDocument();
  });

  it('displays correct initial state', () => {
    render(<${componentName} />);
    const element = screen.getByTestId('${componentName.toLowerCase()}');
    expect(element).toBeVisible();
  });

  it('handles user interaction', () => {
    render(<${componentName} />);
    const button = screen.getByRole('button');
    fireEvent.click(button);
    expect(screen.getByText(/clicked/i)).toBeInTheDocument();
  });

  it('updates props correctly', () => {
    const { rerender } = render(<${componentName} value="initial" />);
    expect(screen.getByText('initial')).toBeInTheDocument();

    rerender(<${componentName} value="updated" />);
    expect(screen.getByText('updated')).toBeInTheDocument();
  });
});
`;
}
5. 覆盖率分析和缺口检测
import subprocess
import json

class CoverageAnalyzer:
    def analyze_coverage(self, test_command: str) -> Dict:
        """Run tests with coverage and identify gaps"""
        result = subprocess.run(
            [test_command, '--coverage', '--json'],
            capture_output=True,
            text=True
        )

        coverage_data = json.loads(result.stdout)
        gaps = self.identify_coverage_gaps(coverage_data)

        return {
            'overall_coverage': coverage_data.get('totals', {}).get('percent_covered', 0),
            'uncovered_lines': gaps,
            'files_below_threshold': self.find_low_coverage_files(coverage_data, 80)
        }

    def identify_coverage_gaps(self, coverage: Dict) -> List[Dict]:
        """Find specific lines/functions without test coverage"""
        gaps = []
        for file_path, data in coverage.get('files', {}).items():
            missing_lines = data.get('missing_lines', [])
            if missing_lines:
                gaps.append({
                    'file': file_path,
                    'lines': missing_lines,
                    'functions': data.get('excluded_lines', [])
                })
        return gaps

    def generate_tests_for_gaps(self, gaps: List[Dict]) -> str:
        """Generate tests specifically for uncovered code"""
        tests = []
        for gap in gaps:
            test_code = self.create_targeted_test(gap)
            tests.append(test_code)
        return '\n\n'.join(tests)
6. 生成 mock
def generate_mock_objects(self, dependencies: List[str]) -> str:
    """Generate mock objects for external dependencies"""
    mocks = ['from unittest.mock import Mock, MagicMock, patch\n']

    for dep in dependencies:
        mocks.append(f"@pytest.fixture")
        mocks.append(f"def mock_{dep}():")
        mocks.append(f"    mock = Mock(spec={dep})")
        mocks.append(f"    mock.method.return_value = 'mocked_result'")
        mocks.append(f"    return mock\n")

    return '\n'.join(mocks)

输出格式

  1. 测试文件:可直接运行的完整测试套件
  2. 覆盖率报告:当前覆盖率及已发现的缺口
  3. Mock 对象:外部依赖的 fixture
  4. 测试文档:测试场景说明
  5. CI 集成:在流水线中运行测试的命令

重点生成易维护、全面的测试,尽早发现 Bug,并增强对代码变更的信心。

限制

  • 仅在任务明确符合上述范围时使用此技能。
  • 不要将输出视为环境专属验证、测试或专家审查的替代品。
  • 如果缺少必要输入、权限、安全边界或成功标准,请停止并请求澄清。

评论

支持 Markdown