AgentSkillsCN

unit-test-generator

自动为函数、类与模块生成全面的单元测试。当您需要为 Python(pytest、unittest)或 Java(JUnit、TestNG)代码创建测试时,可使用此功能。它会生成覆盖范围全面的测试,包括正常路径、边缘案例以及错误条件。它会分析代码库中已有的测试模式,以匹配风格与规范。它支持模拟、参数化测试、固定装置,并遵循各框架的最佳实践。

SKILL.md
--- frontmatter
name: unit-test-generator
description: Automatically generates comprehensive unit tests for functions, classes, and modules. Use when you need to create tests for Python (pytest, unittest) or Java (JUnit, TestNG) code. Generates tests with comprehensive coverage including happy paths, edge cases, and error conditions. Analyzes existing test patterns in the codebase to match style and conventions. Supports mocking, parameterized tests, fixtures, and follows best practices for each framework.

Unit Test Generator

Automatically generate comprehensive unit tests for your code.

Core Capabilities

This skill helps you generate high-quality unit tests by:

  1. Analyzing source code - Understanding function/class behavior and contracts
  2. Identifying test cases - Determining happy paths, edge cases, and error conditions
  3. Matching style - Following existing test patterns and conventions in your codebase
  4. Generating tests - Creating complete, runnable test code
  5. Explaining coverage - Documenting what each test validates

Test Generation Workflow

Step 1: Analyze the Code to Test

Examine the source code to understand:

Function Signature:

  • Parameters and their types
  • Return type
  • Exceptions raised

Function Behavior:

  • What the function does
  • Preconditions and postconditions
  • Side effects (DB writes, API calls, file I/O)
  • Dependencies on other code

Example Analysis:

python
def calculate_discount(price: float, discount_percent: float) -> float:
    """Calculate discounted price.

    Args:
        price: Original price (must be positive)
        discount_percent: Discount percentage (0-100)

    Returns:
        Discounted price

    Raises:
        ValueError: If price is negative or discount is invalid
    """
    if price < 0:
        raise ValueError("Price cannot be negative")
    if not 0 <= discount_percent <= 100:
        raise ValueError("Discount must be between 0 and 100")

    return price * (1 - discount_percent / 100)

Analysis:

  • Takes two floats, returns float
  • Validates price >= 0
  • Validates discount in [0, 100]
  • Raises ValueError for invalid inputs
  • Pure function (no side effects)

Step 2: Identify Test Cases

Determine all test scenarios using the Comprehensive Coverage approach:

1. Happy Path Tests - Normal, expected usage

  • Valid inputs that should succeed
  • Typical use cases

2. Edge Case Tests - Boundary conditions

  • Zero values
  • Maximum/minimum values
  • Empty inputs
  • Single element inputs

3. Error Condition Tests - Invalid inputs

  • Null/None values
  • Negative numbers (when positive expected)
  • Out-of-range values
  • Type mismatches (if applicable)
  • Invalid states

4. Special Cases - Domain-specific scenarios

  • Floating point precision
  • String encoding issues
  • Date/time edge cases (leap years, time zones)
  • Concurrency issues

Example Test Cases for calculate_discount:

CategoryTest CaseInputExpected
Happy pathNormal discountprice=100, discount=2080.0
Happy pathNo discountprice=100, discount=0100.0
Happy pathFull discountprice=100, discount=1000.0
Edge caseZero priceprice=0, discount=500.0
Edge caseSmall discountprice=100, discount=0.0199.99
ErrorNegative priceprice=-10, discount=20ValueError
ErrorDiscount too highprice=100, discount=101ValueError
ErrorNegative discountprice=100, discount=-5ValueError

Step 3: Examine Existing Test Patterns

Before generating tests, analyze existing tests in the codebase to match style:

Look for:

  • Test file naming convention (test_*.py, *_test.py, *Test.java)
  • Test class structure (if used)
  • Assertion style (assert, self.assertEqual, assertThat)
  • Fixture/setup patterns
  • Mocking patterns
  • Test organization (Arrange-Act-Assert, Given-When-Then)
  • Naming conventions (test_function_does_something, testFunctionDoesSomething)

Python Example - Analyze Existing Tests:

python
# If existing tests use this pattern:
class TestUserService:
    @pytest.fixture
    def user_service(self):
        return UserService()

    def test_create_user_with_valid_data_succeeds(self, user_service):
        # Arrange
        user_data = {"name": "Alice", "email": "alice@example.com"}

        # Act
        user = user_service.create_user(user_data)

        # Assert
        assert user.name == "Alice"
        assert user.email == "alice@example.com"

Pattern Identified:

  • Class-based test organization
  • pytest fixtures
  • Descriptive test names with underscores
  • Arrange-Act-Assert comments
  • Direct assertions using assert

Java Example - Analyze Existing Tests:

java
// If existing tests use this pattern:
public class UserServiceTest {
    private UserService userService;

    @Before
    public void setUp() {
        userService = new UserService();
    }

    @Test
    public void testCreateUserWithValidData() {
        // given
        UserData data = new UserData("Alice", "alice@example.com");

        // when
        User user = userService.createUser(data);

        // then
        assertEquals("Alice", user.getName());
        assertEquals("alice@example.com", user.getEmail());
    }
}

Pattern Identified:

  • JUnit 4 style with @Before setup
  • Given-When-Then comments
  • assertEquals assertions
  • Test method prefix test

Step 4: Generate Test Code

Create complete, runnable tests following the identified patterns.

Test Structure Template:

code
1. Test file/class setup
2. Fixtures/setup methods (if needed)
3. Happy path tests
4. Edge case tests
5. Error condition tests
6. Cleanup/teardown (if needed)

Python Example - Generated Tests:

See references/test_patterns.md for full example with 9 comprehensive tests covering happy paths, edge cases, and error conditions.

Java Example - Generated Tests:

See references/test_patterns.md for full JUnit example with comprehensive coverage.

Step 5: Handle Dependencies and Mocking

When the code under test has dependencies (databases, APIs, external services), generate tests with appropriate mocks.

Identify Dependencies:

  • External API calls
  • Database queries
  • File system operations
  • Time/date dependencies
  • Random number generation
  • Other service classes

Python Mocking Pattern:

python
@pytest.fixture
def mock_dependency():
    return Mock()

def test_with_mock(mock_dependency):
    # Arrange
    mock_dependency.method.return_value = expected_value

    # Act
    result = code_under_test(mock_dependency)

    # Assert
    mock_dependency.method.assert_called_once()
    assert result == expected_value

Java Mocking Pattern (Mockito):

java
@Mock
private Dependency dependency;

@Test
public void testWithMock() {
    // given
    when(dependency.method()).thenReturn(expectedValue);

    // when
    Result result = codeUnderTest(dependency);

    // then
    verify(dependency).method();
    assertEquals(expectedValue, result);
}

For detailed mocking examples, see references/test_patterns.md.

Step 6: Add Documentation and Coverage Summary

Include comments explaining:

  • What each test validates
  • Why edge cases are important
  • Coverage achieved

Coverage Summary Example:

python
"""
Test coverage for calculate_discount function:

Happy Path (3 tests):
- Normal discount calculation
- Zero discount (no change)
- Full discount (price becomes 0)

Edge Cases (3 tests):
- Zero price
- Very small discount percentage
- Large price values

Error Conditions (3 tests):
- Negative price
- Discount > 100%
- Negative discount

Total: 9 tests covering all execution paths
"""

Advanced Patterns

Parameterized Tests

For testing multiple similar scenarios efficiently.

Python (pytest):

python
@pytest.mark.parametrize("price,discount,expected", [
    (100, 20, 80),
    (100, 0, 100),
    (100, 100, 0),
    (50, 10, 45),
    (200, 25, 150),
])
def test_calculate_discount_various_inputs(price, discount, expected):
    result = calculate_discount(price, discount)
    assert result == expected

Java (JUnit 5):

java
@ParameterizedTest
@CsvSource({
    "100.0, 20.0, 80.0",
    "100.0, 0.0, 100.0",
    "100.0, 100.0, 0.0"
})
void testCalculateDiscountVariousInputs(double price, double discount, double expected) {
    assertEquals(expected, calculator.calculateDiscount(price, discount), 0.001);
}

Testing Classes with State

For classes that maintain state across method calls, see references/test_patterns.md for complete examples of:

  • Fixture setup for stateful objects
  • Testing state transitions
  • Testing side effects
  • Cleanup and teardown

Framework-Specific Guidance

Python (pytest)

Key patterns:

  • Use @pytest.fixture for setup/teardown
  • Use @pytest.mark.parametrize for data-driven tests
  • Use pytest.raises() for exception testing
  • Use pytest.approx() for floating point comparisons
  • Use mocker fixture (pytest-mock) for mocking

Test file naming: test_*.py or *_test.py

Python (unittest)

Key patterns:

  • Inherit from unittest.TestCase
  • Use setUp() and tearDown() methods
  • Use self.assertEqual(), self.assertTrue(), etc.
  • Use self.assertRaises() for exceptions
  • Use unittest.mock for mocking

Java (JUnit 4)

Key patterns:

  • Use @Before and @After for setup/teardown
  • Use @Test annotation on test methods
  • Use assertEquals(), assertTrue(), etc.
  • Use @Test(expected = Exception.class) for exceptions
  • Use Mockito for mocking

Java (JUnit 5)

Key patterns:

  • Use @BeforeEach and @AfterEach
  • Use @Test annotation
  • Use assertEquals(), assertThrows(), etc.
  • Use @ParameterizedTest for data-driven tests
  • Use @ExtendWith(MockitoExtension.class) for Mockito

Best Practices

  1. One assertion per test (generally) - Makes failures clear
  2. Test behavior, not implementation - Tests should survive refactoring
  3. Use descriptive test names - Name should explain what and why
  4. Follow AAA pattern - Arrange, Act, Assert (or Given-When-Then)
  5. Keep tests independent - Tests shouldn't depend on each other
  6. Mock external dependencies - Tests should be fast and reliable
  7. Test edge cases - Don't just test the happy path
  8. Use fixtures/setup wisely - Share setup but avoid complex fixtures
  9. Verify error messages - Not just exception type
  10. Keep tests simple - If test is complex, code might be too

Resources

  • references/test_patterns.md - Complete examples for common scenarios (mocking, stateful classes, async code, database operations, file I/O)
  • references/assertion_guide.md - Framework-specific assertion reference and best practices

Quick Reference

ScenarioPython (pytest)Java (JUnit)
Basic testdef test_name():@Test public void testName()
Setup@pytest.fixture@Before (JUnit 4) or @BeforeEach (JUnit 5)
Exception testwith pytest.raises(Error):@Test(expected = Error.class) or assertThrows()
Parameterized@pytest.mark.parametrize@ParameterizedTest
Mockingmocker.patch() or Mock()@Mock with Mockito
Float comparisonpytest.approx()assertEquals(x, y, delta)