Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Generate or extend comprehensive test suites — unit, integration, E2E, and contract tests — for any language or framework. Use when the user asks to write tests, add coverage, test a specific function or module, set up a test framework, generate test cases from code, or validate behaviour with automated tests.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 67% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 178% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 199% | 0% |
| case-01 | ✓→✗ | ▼ Worse | 51% | 0% |
| case-09 | ✓→✗ | ▼ Worse | 173% | 0% |
Approach every test as a senior engineer who has been burned by tests that passed but shipped broken software. Tests are not a checkbox — they are executable specifications of what the code must do. A test suite that gives false confidence is worse than no tests at all.
Your job is not to maximize line coverage. Your job is to make it impossible for a regression to ship undetected.
if, every try/catch, every early return is a branch that needs coverageWhat: Test a single function or class in complete isolation. All dependencies mocked. When: Business logic, utility functions, data transformations, validation, algorithms. Speed target: < 50ms per test. Should run in seconds for the full suite. Rule: If it touches the network, disk, or a real database, it is not a unit test.
What: Test multiple components working together. May use a real database (container) or real file system. When: Repository layer, service layer wired to a real DB, message queue consumers, API handlers end-to-end. Speed target: < 5 seconds per test. Run in CI, not on every file save. Rule: Each test must clean up its own data. Never leave test state in the DB.
What: Test the full system from the user's perspective — real browser, real HTTP, real database. When: Critical user journeys (sign up, checkout, core workflow). Not for every feature. Tools: Playwright (preferred), Cypress. Rule: Keep the E2E suite small and focused on journeys that cannot be caught by integration tests.
What: Verify that a service's API matches the contract its consumers depend on. When: Microservices, public APIs, SDK interfaces, any boundary where two teams own either side. Tools: Pact (consumer-driven contract testing). Rule: Contract tests live with the consumer. The provider runs them in CI before merging.
What: Capture rendered output and alert when it changes. When: UI components, serialised data structures, generated files. Rule: Snapshots must be reviewed on update — never auto-accept snapshot diffs without reading them.
Every test follows this structure, always:
describe('<ModuleName>', () => {
describe('<methodName>', () => {
it('<does something specific> when <condition>', () => {
// ARRANGE — set up all inputs, mocks, and state
const input = ...
mockDependency.returns(...)
// ACT — call the thing under test, once
const result = functionUnderTest(input)
// ASSERT — verify the outcome, one concept
expect(result).toEqual(expectedOutput)
})
})
})Naming rules:
it('returns null when user is not found')it('works') or it('test 1')For every function or method, generate tests covering:
Happy Path
Input Validation
Error / Failure Paths
Business Logic Edge Cases
Mock at the boundary, not in the middle.
Date.now(), new Date()) whenever time affects behaviourMath.random(), uuid()) when IDs or tokens affect assertionsMock fidelity rules:
{} when the real dependency returns { id: string, name: string } will mask real bugsFramework-specific mocking:
typescript// Jest / Vitest jest.mock('./emailService') vi.spyOn(userRepository, 'findById').mockResolvedValue(mockUser) // Python (pytest) @patch('app.services.email.send') def test_sends_welcome_email(mock_send): ... // Go type MockRepo struct{ ... } func (m *MockRepo) FindByID(id string) (*User, error) { ... }
Use factories, never raw literals.
typescript// Bad — brittle, hard to maintain, hides what matters const user = { id: '123', name: 'John', email: 'john@example.com', role: 'admin', createdAt: new Date() } // Good — factory with sensible defaults, override only what matters for this test const user = createUser({ role: 'admin' })
Factory rules:
Database fixtures:
beforeEach / setup, destroyed in afterEach / teardownCoverage is a floor, not a goal.
| Layer | Minimum coverage | |-------|-----------------| | services/ (business logic) | 80% line, 70% branch | | utils/ (pure functions) | 90% line | | Auth / payments / security paths | 100% line, 100% branch | | api/ (route handlers) | 70% line (integration tests cover the rest) | | repositories/ (data access) | Covered by integration tests, not unit tests |
Coverage does not mean correctness. 100% line coverage with weak assertions is worthless. Always ask: does this test actually catch a regression if the implementation changes?
Mutation testing — the gold standard: tools like Stryker (JS/TS), mutmut (Python), or go-mutesting modify the source code and check if tests detect the change. If a mutant survives, the test suite has a blind spot.
Every test suite must be runnable in CI with a single command:
bash# Examples by stack bun test # Bun npm test / yarn test # Node pytest # Python go test ./... # Go ./gradlew test # Java/Kotlin dotnet test # C# cargo test # Rust
CI pipeline rules:
typescript// Jest / Vitest structure import { describe, it, expect, beforeEach, vi } from 'vitest' describe('UserService', () => { let userService: UserService let mockRepo: MockUserRepository beforeEach(() => { mockRepo = createMockUserRepository() userService = new UserService(mockRepo) }) describe('findById', () => { it('returns the user when found', async () => { const expected = createUser({ id: '1' }) mockRepo.findById.mockResolvedValue(expected) const result = await userService.findById('1') expect(result).toEqual(expected) }) it('throws UserNotFoundError when user does not exist', async () => { mockRepo.findById.mockResolvedValue(null) await expect(userService.findById('99')).rejects.toThrow(UserNotFoundError) }) }) })
pythonimport pytest from unittest.mock import AsyncMock, patch from app.services.user import UserService from tests.factories import create_user class TestUserService: @pytest.fixture(autouse=True) def setup(self): self.mock_repo = AsyncMock() self.service = UserService(repo=self.mock_repo) async def test_find_by_id_returns_user_when_found(self): expected = create_user(id="1") self.mock_repo.find_by_id.return_value = expected result = await self.service.find_by_id("1") assert result == expected async def test_find_by_id_raises_when_not_found(self): self.mock_repo.find_by_id.return_value = None with pytest.raises(UserNotFoundError): await self.service.find_by_id("99")
gofunc TestUserService_FindByID(t *testing.T) { t.Run("returns user when found", func(t *testing.T) { expected := fixtures.CreateUser(t, fixtures.WithID("1")) mockRepo := &MockUserRepo{FindByIDFunc: func(id string) (*User, error) { return expected, nil }} svc := NewUserService(mockRepo) result, err := svc.FindByID("1") require.NoError(t, err) assert.Equal(t, expected, result) }) t.Run("returns error when user not found", func(t *testing.T) { mockRepo := &MockUserRepo{FindByIDFunc: func(id string) (*User, error) { return nil, ErrNotFound }} svc := NewUserService(mockRepo) _, err := svc.FindByID("99") assert.ErrorIs(t, err, ErrNotFound) }) }
A test suite is not done until:
npm test / pytest / go test ./... passes with zero failuresit.only, fit, fdescribe, test.only, pytest.mark.only left in committed codeOther measured skills in the registry, with their headline benchmark lift.