| name | test-generator |
| description | Generate unit and integration tests for existing code: happy path, edge cases, error handling, mocks — for any language or framework |
Test Generator Skill
When to activate
- Adding tests to untested code (greenfield or legacy)
- A new function was written and needs test coverage
- Code review feedback says "add tests for X"
- Increasing coverage on a specific file or module
- Writing tests before a refactor to establish a safety net
When NOT to use
- TDD (test-first) — write the tests yourself first, then implement
- E2E tests for UI flows — use the browser skill with Playwright instead
- Load/performance tests — use dedicated tools (k6, Artillery, Locust)
- When the function is so simple that tests add no signal
Instructions
What to tell Claude
/test-generator
File: {path/to/file.py or paste the function}
Framework: {pytest / jest / vitest / go test / JUnit / RSpec / etc}
Test for: {all exported functions / just `functionName` / the whole module}
Coverage goals: {happy path only / edge cases / error handling / all}
Or just:
/test-generator
Write tests for the `calculateDiscount()` function in src/billing/discounts.ts.
Include: happy path, zero quantity, negative quantity, max discount cap, rounding edge cases.
Test anatomy Claude follows
def test_{what}_{scenario}():
...
result = function_under_test(inputs)
assert result == expected
describe('calculateDiscount', () => {
it('applies percentage discount to base price', () => {
const price = 100
const discountPct = 20
const result = calculateDiscount(price, discountPct)
expect(result).toBe(80)
})
})
Coverage checklist Claude works through
For every function under test:
Mocking patterns
Python (pytest + unittest.mock):
from unittest.mock import patch, MagicMock
def test_send_email_calls_smtp(mock_smtp):
with patch('myapp.email.smtplib.SMTP') as mock_smtp:
mock_instance = MagicMock()
mock_smtp.return_value.__enter__ = MagicMock(return_value=mock_instance)
send_email('user@example.com', 'Hello')
mock_instance.send_message.assert_called_once()
TypeScript (jest):
jest.mock('../db/userRepository')
import { findUser } from '../db/userRepository'
const mockFindUser = findUser as jest.MockedFunction<typeof findUser>
it('returns 404 when user not found', async () => {
mockFindUser.mockResolvedValue(null)
const res = await request(app).get('/users/999')
expect(res.status).toBe(404)
})
Go:
type MockEmailer struct{ called bool }
func (m *MockEmailer) Send(to, body string) error {
m.called = true
return nil
}
func TestNotifyUser_CallsEmailer(t *testing.T) {
mock := &MockEmailer{}
NotifyUser("user@example.com", mock)
assert.True(t, mock.called)
}
Test naming conventions
| Pattern | Example |
|---|
test_{function}_{scenario} (pytest) | test_calculate_discount_applies_cap |
it('{does something}') (jest) | it('returns null when user not found') |
Test{Function}{Scenario} (Go) | TestCalculateDiscountAppliesCap |
{method}_{scenario}__{expected} (NUnit) | Calculate_NegativeInput__ThrowsArgumentException |
Integration tests
For functions that hit the database or external services:
@pytest.mark.integration
def test_create_user_persists_to_db(db_session):
user = create_user(db_session, email="alice@example.com", password="secret")
found = db_session.query(User).filter_by(email="alice@example.com").first()
assert found is not None
assert found.id == user.id
Principle: Integration tests use real dependencies (test DB, in-memory queue). Unit tests mock everything outside the function boundary.
Snapshot tests (UI / serialization)
it('renders the UserCard correctly', () => {
const { container } = render(<UserCard name="Alice" role="Admin" />)
expect(container).toMatchSnapshot()
})
Use sparingly — snapshots become noise when they're too large or change too often.
Example
Input function:
def apply_coupon(price: float, coupon_code: str) -> float:
"""Apply a coupon code to a price. Raises ValueError for invalid codes."""
coupons = {"SAVE10": 0.10, "SAVE25": 0.25, "HALF": 0.50}
if coupon_code not in coupons:
raise ValueError(f"Invalid coupon code: {coupon_code}")
return round(price * (1 - coupons[coupon_code]), 2)
Expected tests:
import pytest
from billing.coupons import apply_coupon
def test_apply_coupon_ten_percent():
assert apply_coupon(100.0, "SAVE10") == 90.0
def test_apply_coupon_twenty_five_percent():
assert apply_coupon(100.0, "SAVE25") == 75.0
def test_apply_coupon_half_price():
assert apply_coupon(100.0, "HALF") == 50.0
def test_apply_coupon_rounds_to_two_decimal_places():
assert apply_coupon(10.0, "SAVE10") == 9.0
def test_apply_coupon_raises_on_invalid_code():
with pytest.raises(ValueError, match="Invalid coupon code: BOGUS"):
apply_coupon(100.0, "BOGUS")
def test_apply_coupon_raises_on_empty_code():
with pytest.raises(ValueError):
apply_coupon(100.0, "")
def test_apply_coupon_with_decimal_price():
assert apply_coupon(99.99, "SAVE10") == 89.99