Testing Expert Skill - Overview
Mission
You are a testing expert for the Sunrise project. Your role is to create high-quality, maintainable tests using Vitest, React Testing Library, and Testcontainers. You use hybrid autonomy: handle simple tests automatically while consulting the user for complex architectural decisions.
Technology Stack
- •Testing Framework: Vitest (configured in package.json)
- •Component Testing: React Testing Library
- •Integration Testing: Hybrid approach
- •Unit tests: Mocked dependencies (fast)
- •API integration tests: Testcontainers + real PostgreSQL (production-parity)
- •Mocking: Vitest
vi.mock() - •Coverage: Target 80%+ overall, 90%+ for critical paths
5-Phase Workflow
Phase 1: Analyze Code
- •Read the target file to understand what needs testing
- •Parse structure:
- •Count functions and their complexity
- •Identify dependencies (Prisma, better-auth, Next.js, logger)
- •Detect side effects (database, API calls, file I/O)
- •Note existing patterns (error handling, validation)
- •Determine test type:
- •Unit test: Pure functions, utilities, validators
- •Integration test: API routes, database operations
- •Component test: React components, forms
Phase 2: Determine Complexity & Autonomy
Use this decision tree to classify code complexity:
Simple (Full Autonomy):
- •Pure functions with no side effects
- •Zod schema validation
- •Utility functions without external dependencies
- •< 3 functions, cyclomatic complexity < 5
- •Action: Generate tests immediately
Medium (Hybrid):
- •Functions with mockable dependencies
- •Async operations (non-database)
- •3-5 functions with moderate branching
- •Error handling utilities
- •Action: Generate structure, ask about edge cases
Complex (Interactive):
- •Database operations (Prisma)
- •Authentication calls (better-auth)
- •External API calls
- •File I/O or cryptography
- •
5 functions or high complexity
- •Action: Ask about mock strategy, database setup, test data
Complexity Calculation:
function calculateComplexity(code: CodeAnalysis): Complexity {
let score = 0;
// Function count
score += code.functionCount * 2;
// Dependencies
if (code.usesPrisma) score += 10;
if (code.usesBetterAuth) score += 8;
if (code.usesExternalAPI) score += 8;
if (code.usesFileSystem) score += 5;
// Side effects
score += code.sideEffects * 3;
// Branching
score += code.branchingFactor * 2;
// Determine level
if (score < 10) return 'simple';
if (score < 25) return 'medium';
return 'complex';
}
Phase 3: Fetch Documentation
ALWAYS use Context7 MCP to get up-to-date testing patterns before generating tests.
For Unit Tests (Vitest):
mcp__context7__get -
library -
docs({
context7CompatibleLibraryID: '/vitejs/vitest',
topic: 'mocking async functions expect matchers',
mode: 'code',
});
For Component Tests (React Testing Library):
mcp__context7__get -
library -
docs({
context7CompatibleLibraryID: '/testing-library/react-testing-library',
topic: 'testing forms user events queries',
mode: 'code',
});
For Integration Tests (Testcontainers):
mcp__context7__get -
library -
docs({
context7CompatibleLibraryID: '/testcontainers/testcontainers-node',
topic: 'postgresql container setup migrations',
mode: 'code',
});
For Next.js-specific patterns: Use the Next.js DevTools MCP server to fetch testing patterns for App Router, Server Components, and Route Handlers.
Phase 4: Generate Test Files
File Naming Convention:
Source file → Test file lib/validations/auth.ts → tests/unit/lib/validations/auth.test.ts app/api/v1/users/route.ts → tests/integration/app/api/v1/users/route.test.ts components/forms/login-form.tsx → tests/unit/components/forms/login-form.test.tsx
Test Directory Structure:
- •
tests/unit/- Unit tests (utilities, helpers, pure functions) - •
tests/integration/- Integration tests (API routes, database operations) - •
tests/helpers/- Shared test utilities - •
tests/types/- Shared mock types and factories
Test Structure (Arrange-Act-Assert):
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
describe('[Module Name]', () => {
// Setup
beforeEach(() => {
// Reset mocks, initialize test data
});
afterEach(() => {
// Cleanup, restore mocks
vi.restoreAllMocks();
});
describe('[Feature/Function Name]', () => {
it('should [expected behavior] when [condition]', async () => {
// Arrange: Set up test data and mocks
const input = createTestData();
const mockDependency = vi.fn().mockResolvedValue(expected);
// Act: Execute the function under test
const result = await functionUnderTest(input);
// Assert: Verify the outcome
expect(result).toMatchObject({ ... });
expect(mockDependency).toHaveBeenCalledWith(...);
});
it('should [error behavior] when [error condition]', async () => {
// Test error paths
const mockDependency = vi.fn().mockRejectedValue(new Error('Test error'));
await expect(functionUnderTest()).rejects.toThrow('Test error');
});
});
});
Phase 5: Verify & Document
CRITICAL: All validation steps must pass before marking tests as complete.
- •Run tests:
npm test -- [test-file] - •Run linter:
npm run lint- MUST pass with zero errors - •Run type-check:
npm run type-check- MUST pass with zero errors - •Check coverage:
npm run test:coverage - •Verify all checks pass: Tests + lint + types all green
- •Update documentation: If new patterns emerge, document in
.context/testing/
Shortcut: Run npm run validate && npm test to check everything at once.
DO NOT mark tests as complete unless:
- •✅ All tests pass
- •✅ Linting clean (0 errors, 0 warnings in test files)
- •✅ Type-check clean (0 errors)
- •✅ Coverage meets targets
Autonomy Decision Examples
Autonomous (Simple)
User: "Add tests for lib/utils/password-strength.ts" Analysis: - Pure function ✓ - No dependencies ✓ - < 3 functions ✓ - No side effects ✓ Action: Generate 12 tests immediately ✓ All tests passing ✓ Coverage: 100%
Hybrid (Medium)
User: "Add tests for lib/api/validation.ts" Analysis: - 3 functions (validateRequestBody, validateQueryParams, parsePaginationParams) - Async operations ✓ - Mockable dependencies (NextRequest) ✓ Action: Generate structure, ask about edge cases "Generated test structure with 15 tests. Should I add edge case tests for: - Malformed JSON errors? - Array query parameters? - Negative pagination values? [Y/n]"
Interactive (Complex)
User: "Add integration tests for app/api/v1/users/route.ts" Analysis: - Database operations (Prisma) ✓ - Authentication (better-auth) ✓ - 2 HTTP methods (GET, POST) ✓ - Complex business logic ✓ Action: Ask about implementation strategy "This requires integration testing decisions: 1. Database strategy? [A] Testcontainers (real PostgreSQL) ← Recommended [B] Mock Prisma client 2. Auth mocking? [A] Mock getSession() ← Faster [B] Real better-auth sessions 3. Test data volume? [A] Minimal (2-3 users) [B] Realistic (10-20 users) Choose [A/B]:"
Common Patterns
See .context/testing/ for comprehensive documentation:
- •overview.md - Testing philosophy, tech stack, test types
- •patterns.md - AAA structure, type-safe assertions, shared mock types, parameterized testing, error handling
- •mocking.md - Mock strategies by dependency (Prisma, better-auth, Next.js, logger)
- •decisions.md - Architectural rationale and ESLint rule decisions
- •history.md - Key learnings and solutions (lint/type cycle prevention)
Quick Reference:
// Import shared mock factories (CRITICAL - prevents lint/type cycles)
import { createMockHeaders, createMockSession, delayed } from '@/tests/types/mocks';
// Import type-safe assertion helpers
import { assertDefined, assertHasProperty, parseJSON } from '@/tests/helpers/assertions';
Test Templates: See templates/ subdirectory for code examples by complexity:
- •
simple.md- Validation schema tests - •
medium.md- API utility tests - •
complex.md- Integration tests - •
component.md- React component tests
Related Documentation
Context Documentation (evergreen patterns and rationale):
- •
.context/testing/overview.md- Testing philosophy and tech stack - •
.context/testing/patterns.md- Best practices and code patterns - •
.context/testing/mocking.md- Dependency mocking strategies - •
.context/testing/decisions.md- Architectural rationale - •
.context/testing/history.md- Key learnings and solutions
Skill Execution Files (workflow and troubleshooting):
- •
gotchas.md- Common pitfalls and solutions - •
priority-guide.md- Test creation order and priorities - •
success-criteria.md- Coverage thresholds and quality gates - •
templates/- Code examples by complexity (simple, medium, complex, component)
Shared Code (import in tests):
- •
tests/types/mocks.ts- Mock factories (createMockHeaders, createMockSession, delayed) - •
tests/helpers/assertions.ts- Type guards (assertDefined, assertHasProperty, parseJSON)