Help us improve
Share bugs, ideas, or general feedback.
From backend-overture
This skill provides backend testing rules with Vitest, real DB/API integration patterns, and service-level testing conventions. Automatically loaded when writing backend tests, reviewing test quality, or when "backend test", "service test", "API test", "integration test", "database test", or "backend test coverage" are mentioned.
npx claudepluginhub tundraray/overture --plugin backend-overtureHow this skill is triggered — by the user, by Claude, or both
Slash command
/backend-overture:typescript-testing-backendThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
- **Vitest**: Primary test runner
Tests backend APIs with vitest/jest, go test, pytest, cargo test; verifies endpoints, DB changes, errors, collects evidence. Prohibits curl; mandates pre-completion verification.
Provides testing pyramid, unit patterns (AAA, isolation, parameterized, edge cases), and React Testing Library for component tests. Use when writing tests or setting up testing infrastructure.
Guides testing strategies using Jest, Vitest, and Testing Library for unit, integration, and e2e tests, with mocking, fixtures, and TDD workflows.
Share bugs, ideas, or general feedback.
import { describe, it, expect, beforeEach, vi } from 'vitest'vi.mock()Mandatory: Unit test coverage must be 70% or higher
Layer-specific targets:
Metrics: Statements, Branches, Functions, Lines
Unit Tests
Integration Tests
Property-Based Tests (fast-check)
Recommended Principle: Always start code changes with tests
Development Steps:
NG Cases (Test-first not required):
__tests__/ directories or co-located with sourceRecommended: Mock external dependencies in unit tests
For integration tests: Use real dependencies
Fix tests: Wrong expected values, implementation detail coupling, flaky assertions Fix implementation: Valid business rules, edge cases, contract violations When in doubt: Confirm with user
src/
├── users/
│ ├── users.service.ts
│ ├── users.controller.ts
│ ├── __tests__/
│ │ ├── users.service.test.ts
│ │ ├── users.controller.test.ts
│ │ └── users.integration.test.ts
│ └── index.ts
Rationale:
__tests__/ directory convention for backend projects{module}.{layer}.test.ts (e.g., users.service.test.ts){feature}.integration.test.tsRecommended: Keep all tests always active
Avoid: test.skip() or commenting out
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { UserService } from '../users.service'
describe('UserService', () => {
let service: UserService
let mockRepository: { findOne: ReturnType<typeof vi.fn>; save: ReturnType<typeof vi.fn> }
beforeEach(() => {
mockRepository = {
findOne: vi.fn(),
save: vi.fn(),
}
service = new UserService(mockRepository as any)
})
it('should return user when found', async () => {
const expectedUser = { id: '1', name: 'John', email: 'john@example.com' }
mockRepository.findOne.mockResolvedValue(expectedUser)
const result = await service.findById('1')
expect(result).toEqual(expectedUser)
expect(mockRepository.findOne).toHaveBeenCalledWith({ where: { id: '1' } })
})
it('should throw NotFoundError when user not found', async () => {
mockRepository.findOne.mockResolvedValue(null)
await expect(service.findById('999')).rejects.toThrow(NotFoundError)
})
})
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import request from 'supertest'
import { createApp } from '../app'
describe('POST /api/users', () => {
let app: Express
beforeAll(async () => {
app = await createApp({ database: 'test' })
})
afterAll(async () => {
await app.close()
})
it('should create user and return 201', async () => {
const response = await request(app)
.post('/api/users')
.send({ name: 'Jane', email: 'jane@example.com' })
.expect(201)
expect(response.body).toMatchObject({
name: 'Jane',
email: 'jane@example.com',
})
expect(response.body.id).toBeDefined()
})
it('should return 400 for invalid email', async () => {
await request(app)
.post('/api/users')
.send({ name: 'Jane', email: 'not-an-email' })
.expect(400)
})
})
import { describe, it, expect } from 'vitest'
import fc from 'fast-check'
import { parseUserId } from '../utils/parse-user-id'
describe('parseUserId', () => {
it('should round-trip: format(parse(id)) === id for valid UUIDs', () => {
fc.assert(
fc.property(fc.uuid(), (uuid) => {
const parsed = parseUserId(uuid)
expect(parsed.toString()).toBe(uuid)
})
)
})
it('should reject non-UUID strings', () => {
fc.assert(
fc.property(
fc.string().filter((s) => !isValidUuid(s)),
(invalidId) => {
expect(() => parseUserId(invalidId)).toThrow()
}
)
)
})
})
Use hardcoded literal values for assertions:
expect(calculatePrice(100, 0.1)).toBe(110)
expect(formatDate(new Date('2025-01-15'))).toBe('2025-01-15')
expect(user.role).toBe('admin')
Verify final results and outcomes:
expect(mockRepository.save).toHaveBeenCalledWith({ name: 'test', email: 'test@example.com' })
expect(result).toEqual({ id: '1', status: 'created' })
expect(response.statusCode).toBe(201)
Every test must include at least one expect() that validates observable behavior.
Mock only direct external I/O dependencies. Internal utilities use real implementations:
vi.mock('../repositories/user.repository') // External I/O — mock
vi.mock('../clients/email.client') // External API — mock
// Internal validators, formatters, mappers — use real implementations
beforeEach(async () => {
await dataSource.query('BEGIN')
})
afterEach(async () => {
await dataSource.query('ROLLBACK')
})