Agent-Skill-Kit

Unit Test Generation

A skill that automates the creation of comprehensive unit tests for functions or classes, emphasizing coverage of edge cases and assertions.

Purpose

To generate thorough, production-ready unit tests that ensure code reliability and maintainability. This skill helps developers catch regressions early, document expected behavior, and follow testing best practices for their specific framework.

When to Use

Trigger this skill when the user:

Usage

When asked to generate unit tests, follow this process:

  1. Analyze the Code:
    • Identify inputs, outputs, and return types.
    • Understand the function’s purpose and expected behavior.
    • Look for edge cases: empty inputs, null/None values, boundary conditions, large inputs.
    • Identify potential exceptions or error conditions.
    • Note any dependencies (external APIs, databases, file systems) that may need mocking.
  2. Detect the Testing Framework:
    • Check the project for existing test files to determine the framework in use.
    • Default to pytest for Python projects.
    • Default to Jest for JavaScript/TypeScript projects.
    • Use JUnit for Java, RSpec for Ruby, Go testing for Go.
    • Match the existing project style if tests already exist.
  3. Generate Comprehensive Tests:
    • Normal Cases: Cover typical, expected usage scenarios.
    • Edge Cases: Empty collections, zero values, maximum values, boundary conditions.
    • Error Cases: Invalid inputs, exceptions, error handling paths.
    • Type Edge Cases: None/null, wrong types (if dynamically typed).
    • Performance Considerations: Large input tests where applicable.
  4. Follow Best Practices:
    • Descriptive Names: Test names should clearly describe what is being tested and expected outcome.
    • Arrange-Act-Assert (AAA): Structure tests with clear setup, execution, and verification.
    • Independence: Each test should be independent and not rely on other tests.
    • Mocking: Mock external dependencies appropriately.
    • Assertions: Use specific, clear assertions (not just assertTrue).
  5. Output the Tests:
    • Place tests in the appropriate test directory (e.g., tests/, __tests__/, spec/).
    • Follow the project’s naming conventions (e.g., test_*.py, *.test.js).
    • Include necessary imports and fixtures.

Examples

Python Example

User Request: “Write tests for this function”

def calculate_discount(price: float, discount_percent: float) -> float:
    """Calculate the discounted price."""
    if price < 0 or discount_percent < 0:
        raise ValueError("Price and discount must be non-negative")
    if discount_percent > 100:
        raise ValueError("Discount cannot exceed 100%")
    return price * (1 - discount_percent / 100)

AI Response:

import pytest
from your_module import calculate_discount


class TestCalculateDiscount:
    """Tests for the calculate_discount function."""

    # Normal cases
    def test_calculate_discount_with_valid_values(self):
        """Test discount calculation with typical values."""
        assert calculate_discount(100.0, 20.0) == 80.0

    def test_calculate_discount_with_zero_discount(self):
        """Test that zero discount returns original price."""
        assert calculate_discount(50.0, 0.0) == 50.0

    def test_calculate_discount_with_full_discount(self):
        """Test that 100% discount returns zero."""
        assert calculate_discount(100.0, 100.0) == 0.0

    # Edge cases
    def test_calculate_discount_with_zero_price(self):
        """Test discount on zero price."""
        assert calculate_discount(0.0, 50.0) == 0.0

    def test_calculate_discount_with_small_values(self):
        """Test with very small decimal values."""
        result = calculate_discount(0.01, 10.0)
        assert result == pytest.approx(0.009, rel=1e-3)

    def test_calculate_discount_with_large_price(self):
        """Test with large price values."""
        assert calculate_discount(1_000_000.0, 25.0) == 750_000.0

    # Error cases
    def test_calculate_discount_negative_price_raises_error(self):
        """Test that negative price raises ValueError."""
        with pytest.raises(ValueError, match="non-negative"):
            calculate_discount(-10.0, 20.0)

    def test_calculate_discount_negative_discount_raises_error(self):
        """Test that negative discount raises ValueError."""
        with pytest.raises(ValueError, match="non-negative"):
            calculate_discount(100.0, -5.0)

    def test_calculate_discount_over_100_percent_raises_error(self):
        """Test that discount over 100% raises ValueError."""
        with pytest.raises(ValueError, match="exceed 100"):
            calculate_discount(100.0, 150.0)

JavaScript Example

User Request: “Generate Jest tests for this function”

function validateEmail(email) {
  if (typeof email !== 'string') {
    throw new TypeError('Email must be a string');
  }
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return regex.test(email);
}

AI Response:

const { validateEmail } = require('./validators');

describe('validateEmail', () => {
  // Normal cases
  it('should return true for valid email addresses', () => {
    expect(validateEmail('user@example.com')).toBe(true);
    expect(validateEmail('name.surname@domain.org')).toBe(true);
  });

  it('should return false for invalid email addresses', () => {
    expect(validateEmail('invalid-email')).toBe(false);
    expect(validateEmail('missing@domain')).toBe(false);
    expect(validateEmail('@nodomain.com')).toBe(false);
  });

  // Edge cases
  it('should return false for empty string', () => {
    expect(validateEmail('')).toBe(false);
  });

  it('should return false for email with spaces', () => {
    expect(validateEmail('user @example.com')).toBe(false);
  });

  // Error cases
  it('should throw TypeError for non-string input', () => {
    expect(() => validateEmail(null)).toThrow(TypeError);
    expect(() => validateEmail(undefined)).toThrow(TypeError);
    expect(() => validateEmail(123)).toThrow(TypeError);
  });
});

Best Practices

Notes