Bun Testing Skill
Quick Reference
- Framework: bun:test
- File pattern:
*.test.ts inside __tests__ directories
- Module mocking: Use
ModuleMocker from @/__tests__ (see patterns)
- Coverage target: 60–80% (focus on important logic, not 100%)
- Config:
.env.test for test environment variables
Test Utilities (src/tests/)
Before writing custom test helpers, check existing utilities:
createTestApp(basePath, route, middleware[]) - Creates test Hono app with error handler, logger, and optional middleware
ModuleMocker(import.meta.url) - Module mocking utility (see mocking patterns)
post(app, url, body, headers) - POST request helper
get(app, url, headers) - GET request helper
doRequest(app, url, method, body, headers) - Generic request helper
Example:
import { createTestApp, post } from '@/__tests__'
const app = createTestApp('/api/v1/auth', signupRoute, [captchaMiddleware()])
const response = await post(app, '/api/v1/auth/signup', {
email: 'test@example.com',
password: 'SecurePass123!'
})
Test Types
- Unit tests: Mock all dependencies (repositories, services, APIs)
- Integration tests: Use real database, mock external APIs only
- Endpoint tests: Use
createTestApp() with mocked services
Test Priorities
- Correct scenario(s)
- Error handling
- Boundary inputs
- Failure scenarios
Database Testing
For integration tests requiring real database:
- Use
bun run db:up:test (starts test DB on port 5433)
- Use
bun run db:down:test (stops test DB)
- Use
bun run test:with-db (starts DB + runs tests)
- Test DB uses separate Docker container (
smela-db-test) and .env.test config
- Reset DB state between test suites if needed using
bun run db:reset:test
Environment Setup
- Use
.env.test for test-specific variables
- Bun handles env loading natively — no manual dotenv needed
- Minimize mocking
@/env — only mock for special/invalid configs
Mocking Strategy
- Mock only business logic dependencies (repositories, external APIs)
- Use global mocks for shared services (CAPTCHA, email) — don't redefine per test
- No real database or network calls — all I/O must be mocked
- Don't mock encapsulated dependencies — mock the public API/wrapper only
Type Safety
- Minimize
any — prefer proper TypeScript types
- Use type inference when possible
- Use
Partial<T> for mock objects
- Exception: Use
any only for complex mocks where full typing adds unnecessary complexity
Test Structure
Use arrange → act → assert pattern with descriptive test names:
describe('UserService', () => {
it('should return user when found by email', async () => {
// Arrange
const mockUser = { id: 1, email: 'test@example.com' }
mockUserRepo.findByEmail.mockResolvedValue(mockUser)
// Act
const result = await userService.findByEmail('test@example.com')
// Assert
expect(result).toEqual(mockUser)
})
})
Mocking Patterns
For detailed mocking patterns including variable ordering, beforeEach setup, and ModuleMocker usage, see references/mocking-patterns.md.
Cleanup
Always clean up side effects after each test:
afterEach(async () => {
await moduleMocker.clear() // restore mocked modules
vi.clearAllMocks() // or mock.mockClear() for individual mocks
})
1---2name: bun-testing3description: Testing guidelines for Bun/TypeScript projects using bun:test framework. Use when writing tests, creating test files, debugging test failures, setting up mocks, or reviewing test code. Triggers on *.test.ts files, test-related questions, mocking patterns, and coverage discussions.4---5
6# Bun Testing Skill
7
8## Quick Reference
9
10- **Framework**: bun:test
11- **File pattern**: `*.test.ts` inside `__tests__` directories
12- **Module mocking**: Use `ModuleMocker` from `@/__tests__` (see [patterns](references/mocking-patterns.md))
13- **Coverage target**: 60–80% (focus on important logic, not 100%)
14- **Config**: `.env.test` for test environment variables
15
16## Test Utilities (src/__tests__/)
17
18Before writing custom test helpers, check existing utilities:
19
20- **`createTestApp(basePath, route, middleware[])`** - Creates test Hono app with error handler, logger, and optional middleware
21- **`ModuleMocker(import.meta.url)`** - Module mocking utility (see [mocking patterns](references/mocking-patterns.md))
22- **`post(app, url, body, headers)`** - POST request helper
23- **`get(app, url, headers)`** - GET request helper
24- **`doRequest(app, url, method, body, headers)`** - Generic request helper
25
26Example:
27
28```typescript
29import { createTestApp, post } from '@/__tests__'
30
31const app = createTestApp('/api/v1/auth', signupRoute, [captchaMiddleware()])
32const response = await post(app, '/api/v1/auth/signup', {
33 email: 'test@example.com',
34 password: 'SecurePass123!'
35})
36```
37
38## Test Types
39
40- **Unit tests**: Mock all dependencies (repositories, services, APIs)
41- **Integration tests**: Use real database, mock external APIs only
42- **Endpoint tests**: Use `createTestApp()` with mocked services
43
44## Test Priorities
45
461. Correct scenario(s)
472. Error handling
483. Boundary inputs
494. Failure scenarios
50
51## Database Testing
52
53For integration tests requiring real database:
54
55- Use `bun run db:up:test` (starts test DB on port 5433)
56- Use `bun run db:down:test` (stops test DB)
57- Use `bun run test:with-db` (starts DB + runs tests)
58- Test DB uses separate Docker container (`smela-db-test`) and `.env.test` config
59- Reset DB state between test suites if needed using `bun run db:reset:test`
60
61## Environment Setup
62
63- Use `.env.test` for test-specific variables
64- Bun handles env loading natively — no manual dotenv needed
65- Minimize mocking `@/env` — only mock for special/invalid configs
66
67## Mocking Strategy
68
69- Mock only business logic dependencies (repositories, external APIs)
70- Use global mocks for shared services (CAPTCHA, email) — don't redefine per test
71- No real database or network calls — all I/O must be mocked
72- Don't mock encapsulated dependencies — mock the public API/wrapper only
73
74## Type Safety
75
76- Minimize `any` — prefer proper TypeScript types
77- Use type inference when possible
78- Use `Partial<T>` for mock objects
79- Exception: Use `any` only for complex mocks where full typing adds unnecessary complexity
80
81## Test Structure
82
83Use arrange → act → assert pattern with descriptive test names:
84
85```typescript
86describe('UserService', () => {
87 it('should return user when found by email', async () => {
88 // Arrange
89 const mockUser = { id: 1, email: 'test@example.com' }
90 mockUserRepo.findByEmail.mockResolvedValue(mockUser)
91
92 // Act
93 const result = await userService.findByEmail('test@example.com')
94
95 // Assert
96 expect(result).toEqual(mockUser)
97 })
98})
99```
100
101## Mocking Patterns
102
103For detailed mocking patterns including variable ordering, `beforeEach` setup, and ModuleMocker usage, see [references/mocking-patterns.md](references/mocking-patterns.md).
104
105## Cleanup
106
107Always clean up side effects after each test:
108
109```typescript
110afterEach(async () => {
111 await moduleMocker.clear() // restore mocked modules
112 vi.clearAllMocks() // or mock.mockClear() for individual mocks
113})
114```