| name | unit-test-writer |
| description | Writes unit tests for existing source code. Use this when the user asks to create, write, or generate tests for their code, functions, or modules. |
| version | 1.0.0 |
Unit Test Writer
Generate comprehensive unit tests for existing source code.
Overview
This skill analyzes source code files and produces well-structured unit tests that
cover key functionality, edge cases, and error conditions. It adapts to the project's
existing test framework and conventions, producing tests that integrate seamlessly
into the existing test suite.
Steps
-
Read and Analyze Source Code
- Use the Read tool to examine the target source file(s)
- Identify all exported functions, classes, and methods
- Understand input parameters, return types, and side effects
- Note any dependencies that will need to be mocked
-
Identify the Test Framework
- Check
package.json for test dependencies (jest, vitest, mocha, pytest, etc.)
- Look for existing test files to understand conventions
- If no framework is configured, recommend one based on the project type:
- TypeScript/JavaScript: vitest or jest
- Python: pytest
- Go: built-in testing package
- Rust: built-in test module
-
Catalog Testable Units
- List each function/method that needs tests
- For each unit, identify:
- Happy path scenarios (normal inputs, expected outputs)
- Edge cases (empty inputs, boundary values, null/undefined)
- Error conditions (invalid inputs, exceptions, timeouts)
- Prioritize public API surfaces over internal helpers
-
Design Test Cases
- Follow the Arrange-Act-Assert (AAA) pattern
- Group related tests in
describe blocks (or equivalent)
- Name tests descriptively:
should return X when given Y
- Plan mock objects for external dependencies (database, API, filesystem)
- Aim for meaningful coverage, not just line count
-
Write the Test File
- Create the test file following project naming conventions:
*.test.ts / *.test.js for JavaScript/TypeScript
test_*.py / *_test.py for Python
*_test.go for Go
- Import the source module and test utilities
- Implement each test case with clear assertions
- Add setup/teardown blocks if needed (beforeEach, afterEach)
-
Include Mocks and Fixtures
- Mock external dependencies (HTTP clients, databases, file I/O)
- Create test fixtures for complex input data
- Use factory functions for reusable test objects
- Keep mocks minimal; only mock what is necessary
-
Run the Tests
- Execute the test suite using the project's test command
- Verify all tests pass on the first run
- If tests fail, diagnose and fix the issues
- Report the test results to the user
Guidelines
- Match the existing code style and test conventions in the project
- Write tests that are independent and can run in any order
- Avoid testing implementation details; focus on behavior
- Each test should test one thing and have one primary assertion
- Use descriptive test names that read like documentation
- Prefer concrete values over random/generated data in tests
- Do not modify the source code being tested unless there is a clear bug
- If the source code has no clear testable units, suggest refactoring
Test Quality Checklist
Common Patterns
Testing Pure Functions
describe('add', () => {
it('should return the sum of two positive numbers', () => {
expect(add(2, 3)).toBe(5);
});
it('should handle negative numbers', () => {
expect(add(-1, 1)).toBe(0);
});
});
Testing Async Functions
describe('fetchUser', () => {
it('should return user data for valid ID', async () => {
const user = await fetchUser(1);
expect(user.name).toBeDefined();
});
it('should throw for invalid ID', async () => {
await expect(fetchUser(-1)).rejects.toThrow();
});
});
Testing with Mocks
describe('UserService', () => {
it('should call repository with correct ID', () => {
const mockRepo = { findById: vi.fn().mockResolvedValue({ id: 1 }) };
const service = new UserService(mockRepo);
await service.getUser(1);
expect(mockRepo.findById).toHaveBeenCalledWith(1);
});
});
Expected Sequence
- Read the source file(s) to understand the code
- Write the test file with comprehensive test cases
- Bash run the test command to verify tests pass
Error Handling
- If the test framework is not installed, install it first
- If source code is too complex to test directly, suggest breaking it into smaller units
- If tests fail due to missing dependencies, add them and retry
- Report untestable code patterns (tight coupling, global state) as suggestions for improvement