templates/skills/test-patterns/SKILL.md
# Test Patterns Testing patterns for reliable, maintainable, and fast tests. > **Template Usage:** Customize for your test framework (Vitest, Jest, Playwright, etc.) and assertion library. ## Test Structure ```typescript // user.test.ts import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { userService } from '@/services/user.service'; import { createTestUser, cleanupTestData } from '@/tests/helpers'; describe('UserService', () => { let testUserId: string; befor
npx skillsauth add javeedishaq/ai-workflow-orchestrator templates/skills/test-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Testing patterns for reliable, maintainable, and fast tests.
Template Usage: Customize for your test framework (Vitest, Jest, Playwright, etc.) and assertion library.
// user.test.ts
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { userService } from '@/services/user.service';
import { createTestUser, cleanupTestData } from '@/tests/helpers';
describe('UserService', () => {
let testUserId: string;
beforeEach(async () => {
// Setup: Create test data
const user = await createTestUser();
testUserId = user.id;
});
afterEach(async () => {
// Cleanup: Remove test data
await cleanupTestData();
});
describe('findById', () => {
it('should return user when found', async () => {
const result = await userService.findById(testUserId);
expect(result.success).toBe(true);
if (result.success) {
expect(result.data).toBeDefined();
expect(result.data?.id).toBe(testUserId);
}
});
it('should return null when not found', async () => {
const result = await userService.findById('non-existent-id');
expect(result.success).toBe(true);
if (result.success) {
expect(result.data).toBeNull();
}
});
});
describe('create', () => {
it('should create user with valid input', async () => {
const input = {
email: '[email protected]',
name: 'New User',
};
const result = await userService.create(input);
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.email).toBe(input.email);
expect(result.data.name).toBe(input.name);
}
});
it('should fail when email already exists', async () => {
const existingUser = await createTestUser();
const result = await userService.create({
email: existingUser.email,
name: 'Another User',
});
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.code).toBe('EMAIL_EXISTS');
}
});
});
});
// tests/helpers.ts
import { db } from '@/lib/db';
import { faker } from '@faker-js/faker';
// Track created test data for cleanup
const createdIds: { table: string; id: string }[] = [];
export async function createTestUser(overrides = {}) {
const user = await db.user.create({
data: {
email: faker.internet.email(),
name: faker.person.fullName(),
...overrides,
},
});
createdIds.push({ table: 'user', id: user.id });
return user;
}
export async function cleanupTestData() {
// Delete in reverse order to respect FK constraints
for (const { table, id } of createdIds.reverse()) {
try {
await db[table].delete({ where: { id } });
} catch {
// Ignore if already deleted
}
}
createdIds.length = 0;
}
// e2e/user-flow.test.ts
import { test, expect } from '@playwright/test';
test.describe('User Registration Flow', () => {
test('should register new user successfully', async ({ page }) => {
// Navigate
await page.goto('/register');
// Fill form
await page.fill('[name="email"]', '[email protected]');
await page.fill('[name="password"]', 'SecurePass123!');
await page.fill('[name="name"]', 'Test User');
// Submit
await page.click('button[type="submit"]');
// Assert redirect
await expect(page).toHaveURL('/dashboard');
// Assert success state
await expect(page.getByText('Welcome, Test User')).toBeVisible();
});
test('should show error for existing email', async ({ page }) => {
await page.goto('/register');
await page.fill('[name="email"]', '[email protected]');
await page.fill('[name="password"]', 'SecurePass123!');
await page.click('button[type="submit"]');
await expect(page.getByText('Email already in use')).toBeVisible();
});
});
// api/users.test.ts
import { describe, it, expect } from 'vitest';
import { createServer } from '@/server';
describe('POST /api/users', () => {
const app = createServer();
it('should create user with valid data', async () => {
const response = await app.inject({
method: 'POST',
url: '/api/users',
payload: {
email: '[email protected]',
name: 'Test User',
},
headers: {
authorization: 'Bearer valid-token',
},
});
expect(response.statusCode).toBe(201);
expect(response.json()).toMatchObject({
success: true,
data: {
email: '[email protected]',
name: 'Test User',
},
});
});
it('should return 400 for invalid data', async () => {
const response = await app.inject({
method: 'POST',
url: '/api/users',
payload: {
email: 'invalid-email',
},
headers: {
authorization: 'Bearer valid-token',
},
});
expect(response.statusCode).toBe(400);
expect(response.json().success).toBe(false);
});
it('should return 401 without auth', async () => {
const response = await app.inject({
method: 'POST',
url: '/api/users',
payload: { email: '[email protected]' },
});
expect(response.statusCode).toBe(401);
});
});
// tests/mocks.ts
import { vi } from 'vitest';
// Mock external service
vi.mock('@/lib/email', () => ({
sendEmail: vi.fn().mockResolvedValue({ success: true }),
}));
// Mock with implementation
vi.mock('@/lib/stripe', () => ({
createPayment: vi.fn().mockImplementation(async (amount) => ({
id: 'pay_mock_123',
amount,
status: 'succeeded',
})),
}));
// Usage in test
import { sendEmail } from '@/lib/email';
it('should send welcome email after registration', async () => {
await userService.register({ email: '[email protected]' });
expect(sendEmail).toHaveBeenCalledWith({
to: '[email protected]',
template: 'welcome',
});
});
// tests/setup.ts
import { beforeAll, afterAll } from 'vitest';
import { db, resetDatabase } from '@/lib/db';
beforeAll(async () => {
// Use test database
process.env.DATABASE_URL = process.env.TEST_DATABASE_URL;
// Reset to clean state
await resetDatabase();
});
afterAll(async () => {
await db.$disconnect();
});
// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'lcov'],
exclude: [
'node_modules/',
'tests/',
'**/*.d.ts',
'**/*.config.*',
],
thresholds: {
statements: 80,
branches: 80,
functions: 80,
lines: 80,
},
},
},
});
tools
# State Management Patterns Client-side state management patterns for modern applications. > **Template Usage:** Customize for your state library (React Query, Zustand, Jotai, Redux, etc.). ## State Categories | Type | Description | Solution | |------|-------------|----------| | **Server State** | Data from API/database | React Query, SWR | | **Client State** | UI state, user preferences | Zustand, Jotai, useState | | **Form State** | Form inputs, validation | React Hook Form, Formik | | **U
development
# Service Patterns Service layer patterns for clean architecture with proper error handling, logging, and type safety. > **Template Usage:** Customize for your ORM (Prisma, Drizzle, TypeORM, etc.) and logging solution. ## Result Type Pattern Never throw exceptions from services. Always return a Result type. ```typescript // lib/result.ts export type Result<T, E = Error> = | { success: true; data: T } | { success: false; error: E }; export function ok<T>(data: T): Result<T, never> { r
testing
# Row-Level Security Patterns Database security patterns for multi-tenant and user-scoped data. > **Template Usage:** Customize for your database (PostgreSQL, Supabase, etc.) and auth system. ## RLS Fundamentals ### Enable RLS on Tables ```sql -- Enable RLS (required before policies take effect) ALTER TABLE users ENABLE ROW LEVEL SECURITY; ALTER TABLE posts ENABLE ROW LEVEL SECURITY; ALTER TABLE comments ENABLE ROW LEVEL SECURITY; -- Force RLS for table owners too (recommended) ALTER TABLE
development
# Production Readiness Patterns Patterns for deploying and operating applications in production with confidence. > **Template Usage:** Customize health checks, monitoring, and runbooks for your infrastructure (Kubernetes, Vercel, AWS, etc.). ## Health Check Endpoints ### Basic Health Check ```typescript // app/api/health/route.ts import { NextResponse } from 'next/server'; export async function GET() { return NextResponse.json( { status: 'healthy', timestamp: new Date().t