Agent skill
setup-testing
Configure test infrastructure with fixtures, helpers, and enhanced coverage. Use when setting up comprehensive testing. Triggers on "setup testing", "test infrastructure", "test helpers", "test fixtures".
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/setup-testing
SKILL.md
Setup Testing
Configures enhanced test infrastructure including shared fixtures, test helpers for mocking Hono context, and coverage configuration.
Quick Reference
Files created:
tests/config/- Test configuration directorytests/fixtures/- Shared test datatests/helpers/- Test utility functions
When to use: After bootstrapping, before writing tests for services/controllers
Prerequisites
- Project bootstrapped with
bootstrap-project - Vitest installed as a dev dependency
Instructions
Phase 1: Create Test Directory Structure
Step 1: Create Test Directories
mkdir -p tests/config
mkdir -p tests/fixtures
mkdir -p tests/helpers
Phase 2: Enhanced Vitest Configuration
Step 2: Update Vitest Config
Update vitest.config.ts with enhanced coverage settings:
import path from "path";
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
include: ["tests/**/*.test.ts"],
// Per-test setup (optional, create if needed)
// setupFiles: ["./tests/config/setup.ts"],
coverage: {
provider: "v8",
reporter: ["text", "json", "html"],
all: true,
include: ["src/**/*.ts"],
exclude: [
"**/*.test.ts",
"**/coverage/**",
"**/node_modules/**",
"**/dist/**",
"**/scripts/**",
"**/src/server.ts",
"**/src/config/**",
],
},
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
});
Phase 3: Create Test Helpers
Step 3: Create Hono Context Mock Helper
Create tests/helpers/hono-context.helper.ts:
import type { Context } from "hono";
import type { AppEnv } from "@/schemas/app-env.schema";
import type { AuthenticatedUserContextType } from "@/schemas/user.schemas";
interface MockContextOptions {
user?: AuthenticatedUserContextType;
validatedBody?: unknown;
validatedQuery?: unknown;
validatedParams?: unknown;
params?: Record<string, string>;
}
export function createMockContext(
options: MockContextOptions = {},
): Context<AppEnv> {
const mockJson = vi.fn().mockImplementation((data, status = 200) => {
return new Response(JSON.stringify(data), {
status,
headers: { "Content-Type": "application/json" },
});
});
const mockText = vi.fn().mockImplementation((text, status = 200) => {
return new Response(text, { status });
});
return {
var: {
user: options.user,
validatedBody: options.validatedBody,
validatedQuery: options.validatedQuery,
validatedParams: options.validatedParams,
},
req: {
param: (key?: string) => (key ? options.params?.[key] : options.params),
query: () => options.validatedQuery || {},
json: async () => options.validatedBody,
},
json: mockJson,
text: mockText,
} as unknown as Context<AppEnv>;
}
// Import vi from vitest at the top of your test file
import { vi } from "vitest";
Step 4: Create Response Helper
Create tests/helpers/response.helper.ts:
export async function parseJsonResponse<T>(response: Response): Promise<T> {
return response.json() as Promise<T>;
}
export function expectStatus(response: Response, expectedStatus: number): void {
expect(response.status).toBe(expectedStatus);
}
export async function expectJsonResponse<T>(
response: Response,
expectedStatus: number,
): Promise<T> {
expectStatus(response, expectedStatus);
return parseJsonResponse<T>(response);
}
Phase 4: Create Shared Fixtures
Step 5: Create User Fixtures
Create tests/fixtures/user.fixtures.ts:
import type { AuthenticatedUserContextType } from "@/schemas/user.schemas";
export const mockAdminUser: AuthenticatedUserContextType = {
userId: "admin-user-id",
globalRole: "admin",
};
export const mockRegularUser: AuthenticatedUserContextType = {
userId: "regular-user-id",
globalRole: "user",
};
export const mockAnotherUser: AuthenticatedUserContextType = {
userId: "another-user-id",
globalRole: "user",
};
export function createMockUser(
overrides: Partial<AuthenticatedUserContextType> = {},
): AuthenticatedUserContextType {
return {
userId: `user-${Date.now()}`,
globalRole: "user",
...overrides,
};
}
Step 6: Create Common Test Utilities
Create tests/fixtures/index.ts:
export * from "./user.fixtures";
// Factory for creating unique IDs
export function createTestId(prefix: string = "test"): string {
return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
// Factory for creating dates
export function createTestDate(daysAgo: number = 0): Date {
const date = new Date();
date.setDate(date.getDate() - daysAgo);
return date;
}
Phase 5: Create Test Setup File (Optional)
Step 7: Create Global Test Setup
Create tests/config/setup.ts:
import { beforeAll, afterAll, beforeEach, afterEach } from "vitest";
// Global setup before all tests
beforeAll(() => {
// Set test environment variables if needed
process.env.NODE_ENV = "test";
});
// Global teardown after all tests
afterAll(() => {
// Cleanup resources
});
// Reset mocks before each test
beforeEach(() => {
// vi.clearAllMocks(); // Uncomment if using global mocks
});
// Cleanup after each test
afterEach(() => {
// Cleanup test data
});
To use this setup file, uncomment the setupFiles line in vitest.config.ts.
Usage Patterns
Using Mock Context in Controller Tests
import { describe, it, expect, vi, beforeEach } from "vitest";
import { createMockContext } from "../helpers/hono-context.helper";
import { mockRegularUser } from "../fixtures/user.fixtures";
import { NoteController } from "@/controllers/note.controller";
describe("NoteController", () => {
let controller: NoteController;
let mockService: any;
beforeEach(() => {
mockService = {
findAll: vi.fn(),
findById: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
};
controller = new NoteController(mockService);
});
describe("create", () => {
it("should create a note and return 201", async () => {
const mockNote = { id: "1", content: "Test note" };
mockService.create.mockResolvedValue(mockNote);
const context = createMockContext({
user: mockRegularUser,
validatedBody: { content: "Test note" },
});
const response = await controller.create(context);
expect(response.status).toBe(201);
expect(mockService.create).toHaveBeenCalledWith(
{ content: "Test note" },
mockRegularUser,
);
});
});
});
Using Fixtures in Service Tests
import { describe, it, expect, vi, beforeEach } from "vitest";
import { mockAdminUser, mockRegularUser, createTestId } from "../fixtures";
import { NoteService } from "@/services/note.service";
describe("NoteService", () => {
let service: NoteService;
let mockRepository: any;
beforeEach(() => {
mockRepository = {
findAll: vi.fn(),
findById: vi.fn(),
create: vi.fn(),
update: vi.fn(),
remove: vi.fn(),
};
service = new NoteService(mockRepository);
});
it("should allow admin to access any note", async () => {
const noteId = createTestId("note");
const mockNote = {
id: noteId,
content: "Test",
createdBy: mockRegularUser.userId,
};
mockRepository.findById.mockResolvedValue(mockNote);
const result = await service.findById(noteId, mockAdminUser);
expect(result).toEqual(mockNote);
});
});
Files Created Summary
tests/
├── config/
│ └── setup.ts # Global test setup (optional)
├── fixtures/
│ ├── index.ts # Re-exports and common utilities
│ └── user.fixtures.ts # User mock data
└── helpers/
├── hono-context.helper.ts # Mock Hono context creator
└── response.helper.ts # Response parsing utilities
Test Organization Best Practices
Directory Structure
tests/
├── config/ # Test configuration
├── fixtures/ # Shared test data
├── helpers/ # Test utilities
├── controllers/ # Controller tests
├── middlewares/ # Middleware tests
├── repositories/ # Repository tests
├── routes/ # Route integration tests
├── schemas/ # Schema validation tests
└── services/ # Service tests
Naming Conventions
- Test files:
{source-file-name}.test.ts - Describe blocks: Match class/function name
- It blocks: Start with "should" describing expected behavior
Example Test Structure
import { describe, it, expect, vi, beforeEach } from "vitest";
describe("ComponentName", () => {
// Setup
let component: ComponentType;
beforeEach(() => {
// Reset state before each test
});
describe("methodName", () => {
it("should do expected behavior", async () => {
// Arrange
// Act
// Assert
});
it("should handle error case", async () => {
// Arrange
// Act
// Assert
});
});
});
What NOT to Do
- Do NOT put test logic in fixture files (only data)
- Do NOT create fixtures that depend on external state
- Do NOT skip cleanup in afterEach/afterAll hooks
- Do NOT use hardcoded IDs that might collide
- Do NOT import from
src/in fixture files if avoidable
See Also
setup-mongodb- MongoDB test infrastructure with memory servertest-schema- Testing Zod schemastest-resource-service- Testing services with mocked repositoriestest-controller- Testing controllers with mocked servicestest-routes- Integration testing routes
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?