Test Runner Configuration
> **Skill Purpose:** Testing framework setup and test organization patterns
SKILL.md
Full skill instructions
Test Runner Configuration
Skill Purpose: Testing framework setup and test organization patterns
Core Skill Pattern
Objective: Establish comprehensive testing infrastructure with automated test execution, coverage reporting, and quality assurance.
Universal Pattern:
- Define testing strategy and coverage requirements
- Configure test runner and execution environment
- Set up test organization and naming conventions
- Establish test utilities and helper functions
- Create continuous testing and reporting procedures
Key Decisions (Project-Specific):
- Testing framework selection and configuration
- Test types and coverage requirements
- Test organization and structure patterns
- Automation and integration with CI/CD
- Reporting and quality gate integration
Project-Specific Implementation Notes
Customize per project:
- Testing framework based on project needs and team preference
- Coverage requirements based on criticality and resources
- Test organization based on project complexity
- Automation level based on development workflow
- Reporting needs based on team size and stakeholder requirements
Example Implementation (Vitest/Jest with Next.js Pattern)
Note: This is an example pattern using Vitest/Jest. Adapt testing framework and configuration based on your specific project requirements and testing strategy.
Prerequisites (Example)
- Project initialized
- Testing strategy defined
- Test types and coverage requirements established
Example: Vitest/Jest Testing Framework Implementation
Framework-Specific Example: This demonstrates the pattern using Vitest/Jest with Next.js. Adapt for your testing framework and project structure.
1. Install Testing Dependencies
# Install Vitest (recommended for new projects)
npm install -D vitest @vitest/ui @vitest/coverage-v8 @vitest/coverage-istanbul
# Install Jest (alternative for existing projects)
npm install -D jest @jest/environment-jsdom @jest/transform-typescript @jest/globals
# Install testing libraries
npm install -D @testing-library/react @testing-library/jest-dom @testing-library/user-event
npm install -D @testing-library/nextjs
# Install mock utilities
npm install -D msw node-fetch
# Install additional test utilities
npm install -D @types/jest @types/node-fetch
npm install -D jest-environment-jsdom @types/jsdom
2. Configure Vitest (Recommended)
Create vitest.config.ts:
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import { resolve } from 'path';
export default defineConfig({
plugins: [react()],
test: {
// Test environment
environment: 'jsdom',
setupFiles: ['./test/setup.ts'],
// Global configuration
globals: true,
watch: false,
// Coverage configuration
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: [
'node_modules/',
'.next/',
'coverage/',
'**/*.d.ts',
'**/*.config.{js,ts}',
'**/stories/**',
'**/__tests__/**',
'**/test/**',
'**/spec/**',
],
thresholds: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
},
// Test file patterns
include: [
'**/__tests__/**/*.{test,spec}.{js,ts,jsx,tsx}',
'**/*.{test,spec}.{js,ts,jsx,tsx}',
],
exclude: [
'node_modules/',
'.next/',
'dist/',
'build/',
],
// Reporter configuration
reporter: ['verbose', 'json', 'html'],
outputFile: {
json: './test-results/results.json',
html: './test-results/report.html',
},
// Performance settings
threads: true,
isolate: true,
bail: 1,
// Timeout settings
testTimeout: 10000,
hookTimeout: 10000,
},
// Resolve configuration
resolve: {
alias: {
'@': resolve(__dirname, './src'),
'@/components': resolve(__dirname, './src/components'),
'@/lib': resolve(__dirname, './src/lib'),
'@/app': resolve(__dirname, './src/app'),
'@/types': resolve(__dirname, './src/types'),
'@/utils': resolve(__dirname, './src/utils'),
'@/hooks': resolve(__dirname, './src/hooks'),
'@/config': resolve(__dirname, './src/config'),
},
},
});
3. Create Jest Configuration (Alternative)
Create jest.config.js:
const nextJest = require('next/jest');
const createJestConfig = nextJest({
// Provide the path to your Next.js app to load next.config.js and .env files
dir: './',
});
// Add any custom config to be passed to Jest
const customJestConfig = {
setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],
moduleNameMapping: {
'^@/(.*)$': '<rootDir>/src/$1',
},
testEnvironment: 'jsdom',
collectCoverageFrom: [
'src/**/*.{js,jsx,ts,tsx}',
'!src/**/*.d.ts',
'!src/**/*.config.{js,ts}',
'!src/**/stories/**',
'!src/**/__tests__/**',
'!src/**/test/**',
'!src/**/spec/**',
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
coverageReporters: ['text', 'lcov', 'html'],
testMatch: [
'**/__tests__/**/*.{js,jsx,ts,tsx}',
'**/*.{test,spec}.{js,jsx,ts,tsx}',
],
testPathIgnorePatterns: [
'node_modules/',
'.next/',
'dist/',
'build/',
],
transform: {
'^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }],
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
globals: {
'ts-jest': {
tsconfig: 'tsconfig.json',
},
},
};
// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig(customJestConfig);
4. Create Test Setup Files
Create test/setup.ts:
import '@testing-library/jest-dom';
import { beforeAll, afterEach, afterAll } from 'vitest';
import { cleanup } from '@testing-library/react';
import { server } from './mocks/server';
// Setup MSW
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
// Reset request handlers after each test
afterEach(() => {
server.resetHandlers();
cleanup();
});
// Close server after all tests
afterAll(() => server.close());
// Mock Next.js router
vi.mock('next/router', () => ({
useRouter() {
return {
push: vi.fn(),
replace: vi.fn(),
prefetch: vi.fn(),
back: vi.fn(),
beforePopState: vi.fn(),
events: {
on: vi.fn(),
off: vi.fn(),
emit: vi.fn(),
},
};
},
usePathname() {
return '/';
},
useSearchParams() {
return new URLSearchParams();
},
}));
// Mock Next.js image
vi.mock('next/image', () => ({
default: (props: any) => <img {...props} />,
}));
// Mock Next.js dynamic import
vi.mock('next/dynamic', () => ({
default: (fn: any) => fn(),
}));
// Mock IntersectionObserver
global.IntersectionObserver = vi.fn().mockImplementation(() => ({
observe: vi.fn(),
unobserve: vi.fn(),
disconnect: vi.fn(),
}));
// Mock ResizeObserver
global.ResizeObserver = vi.fn().mockImplementation(() => ({
observe: vi.fn(),
unobserve: vi.fn(),
disconnect: vi.fn(),
}));
// Mock matchMedia
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: vi.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: vi.fn(), // deprecated
removeListener: vi.fn(), // deprecated
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
})),
});
// Mock localStorage
const localStorageMock = {
getItem: vi.fn(),
setItem: vi.fn(),
removeItem: vi.fn(),
clear: vi.fn(),
};
vi.stubGlobal('localStorage', localStorageMock);
// Mock sessionStorage
const sessionStorageMock = {
getItem: vi.fn(),
setItem: vi.fn(),
removeItem: vi.fn(),
clear: vi.fn(),
};
vi.stubGlobal('sessionStorage', sessionStorageMock);
// Mock fetch
global.fetch = vi.fn();
// Mock WebSocket
global.WebSocket = vi.fn().mockImplementation(() => ({
close: vi.fn(),
send: vi.fn(),
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
}));
5. Create MSW Mock Server
Create test/mocks/server.ts:
import { setupServer } from 'msw/node';
import { rest } from 'msw';
// Mock API handlers
export const handlers = [
// Authentication endpoints
rest.post('/api/auth/signin', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
user: {
id: 'user-1',
email: '[email protected]',
name: 'Test User',
},
session: {
expires: '2024-12-31T23:59:59.999Z',
},
})
);
}),
rest.post('/api/auth/signup', (req, res, ctx) => {
return res(
ctx.status(201),
ctx.json({
user: {
id: 'user-1',
email: '[email protected]',
name: 'Test User',
},
})
);
}),
rest.post('/api/auth/signout', (req, res, ctx) => {
return res(ctx.status(200));
}),
// User endpoints
rest.get('/api/users/profile', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
id: 'user-1',
email: '[email protected]',
name: 'Test User',
avatar_url: null,
})
);
}),
rest.put('/api/users/profile', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
id: 'user-1',
email: '[email protected]',
name: req.body.name || 'Test User',
avatar_url: req.body.avatar_url || null,
})
);
}),
// Posts endpoints
rest.get('/api/posts', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
posts: [
{
id: 'post-1',
title: 'Test Post 1',
content: 'This is a test post',
published: true,
author_id: 'user-1',
created_at: '2024-01-01T00:00:00.000Z',
updated_at: '2024-01-01T00:00:00.000Z',
},
{
id: 'post-2',
title: 'Test Post 2',
content: 'This is another test post',
published: false,
author_id: 'user-1',
created_at: '2024-01-02T00:00:00.000Z',
updated_at: '2024-01-02T00:00:00.000Z',
},
],
})
);
}),
rest.post('/api/posts', (req, res, ctx) => {
return res(
ctx.status(201),
ctx.json({
id: 'post-new',
title: req.body.title,
content: req.body.content,
published: req.body.published || false,
author_id: 'user-1',
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
})
);
}),
rest.get('/api/posts/:id', (req, res, ctx) => {
const { id } = req.params;
if (id === 'post-1') {
return res(
ctx.status(200),
ctx.json({
id: 'post-1',
title: 'Test Post 1',
content: 'This is a test post',
published: true,
author_id: 'user-1',
created_at: '2024-01-01T00:00:00.000Z',
updated_at: '2024-01-01T00:00:00.000Z',
})
);
}
return res(ctx.status(404));
}),
// Error handlers
rest.get('/api/error', (req, res, ctx) => {
return res(
ctx.status(500),
ctx.json({ error: 'Internal server error' })
);
}),
rest.get('/api/unauthorized', (req, res, ctx) => {
return res(
ctx.status(401),
ctx.json({ error: 'Unauthorized' })
);
}),
];
// Create server
export const server = setupServer(...handlers);
6. Create Test Utilities
Create test/utils.tsx:
import { render, RenderOptions } from '@testing-library/react';
import { ReactElement } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { BrowserRouter } from 'react-router-dom';
import { AuthProvider } from '@/contexts/auth-context';
// Test query client
function createTestQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
retry: false,
gcTime: 0,
},
mutations: {
retry: false,
},
},
});
}
// Custom render function
interface CustomRenderOptions extends Omit<RenderOptions, 'wrapper'> {
queryClient?: QueryClient;
initialRoute?: string;
}
function customRender(
ui: ReactElement,
{
queryClient = createTestQueryClient(),
initialRoute = '/',
...renderOptions
}: CustomRenderOptions = {}
) {
function Wrapper({ children }: { children: React.ReactNode }) {
return (
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<AuthProvider>
{children}
</AuthProvider>
</BrowserRouter>
</QueryClientProvider>
);
}
return {
...render(ui, { wrapper: Wrapper, ...renderOptions }),
queryClient,
};
}
// Re-export everything from Testing Library
export * from '@testing-library/react';
export { customRender as render };
// Mock data generators
export const createMockUser = (overrides = {}) => ({
id: 'user-1',
email: '[email protected]',
name: 'Test User',
avatar_url: null,
created_at: '2024-01-01T00:00:00.000Z',
updated_at: '2024-01-01T00:00:00.000Z',
...overrides,
});
export const createMockPost = (overrides = {}) => ({
id: 'post-1',
title: 'Test Post',
content: 'This is a test post',
published: true,
author_id: 'user-1',
created_at: '2024-01-01T00:00:00.000Z',
updated_at: '2024-01-01T00:00:00.000Z',
...overrides,
});
// Test helpers
export const waitForLoadingToFinish = () =>
new Promise(resolve => setTimeout(resolve, 0));
export const flushPromises = () => new Promise(resolve => setTimeout(resolve, 0));
// Mock functions
export const mockPush = vi.fn();
export const mockReplace = vi.fn();
export const mockPrefetch = vi.fn();
// Mock router
export const mockRouter = {
push: mockPush,
replace: mockReplace,
prefetch: mockPrefetch,
back: vi.fn(),
beforePopState: vi.fn(),
events: {
on: vi.fn(),
off: vi.fn(),
emit: vi.fn(),
},
};
// Async test utilities
export const act = async (callback: () => void | Promise<void>) => {
await callback();
await flushPromises();
};
// Component test helpers
export const renderWithProviders = (
component: ReactElement,
options?: CustomRenderOptions
) => {
return customRender(component, options);
};
// Form test helpers
export const fillForm = async (
getByLabelText: (text: string) => HTMLElement,
formData: Record<string, string>
) => {
for (const [label, value] of Object.entries(formData)) {
const input = getByLabelText(label) as HTMLInputElement;
await act(async () => {
input.value = value;
input.dispatchEvent(new Event('input', { bubbles: true }));
});
}
};
export const submitForm = async (
getByRole: (role: string, options?: any) => HTMLElement
) => {
const submitButton = getByRole('button', { name: /submit/i }) as HTMLButtonElement;
await act(async () => {
submitButton.click();
});
};
7. Create Example Tests
Create test/components/Button.test.tsx:
import { render, screen, fireEvent } from '@testing-library/react';
import { Button } from '@/components/ui/button';
import { createMockUser } from '../utils';
describe('Button Component', () => {
it('renders correctly with default props', () => {
render(<Button>Click me</Button>);
const button = screen.getByRole('button', { name: /click me/i });
expect(button).toBeInTheDocument();
expect(button).toHaveClass('btn');
expect(button).toHaveClass('btn-primary');
});
it('renders with different variants', () => {
render(<Button variant="destructive">Delete</Button>);
const button = screen.getByRole('button', { name: /delete/i });
expect(button).toHaveClass('btn-destructive');
});
it('handles click events', async () => {
const handleClick = vi.fn();
render(<Button onClick={handleClick}>Click me</Button>);
const button = screen.getByRole('button', { name: /click me/i });
await fireEvent.click(button);
expect(handleClick).toHaveBeenCalledTimes(1);
});
it('is disabled when loading', () => {
render(<Button loading>Loading...</Button>);
const button = screen.getByRole('button', { name: /loading/i });
expect(button).toBeDisabled();
});
it('applies custom className', () => {
render(<Button className="custom-class">Click me</Button>);
const button = screen.getByRole('button', { name: /click me/i });
expect(button).toHaveClass('custom-class');
});
});
Create test/hooks/useAuth.test.ts:
import { renderHook, waitFor } from '@testing-library/react';
import { useAuth } from '@/hooks/use-auth';
import { createMockUser } from '../utils';
import { server } from '../mocks/server';
// Mock server responses
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
describe('useAuth Hook', () => {
it('returns initial loading state', () => {
const { result } = renderHook(() => useAuth());
expect(result.current.loading).toBe(true);
expect(result.current.user).toBe(null);
expect(result.current.session).toBe(null);
});
it('loads user session', async () => {
// Mock successful session
server.use(
rest.get('/api/auth/session', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
user: createMockUser(),
expires: '2024-12-31T23:59:59.999Z',
})
);
})
);
const { result } = renderHook(() => useAuth());
await waitFor(() => {
expect(result.current.loading).toBe(false);
expect(result.current.user).toEqual(createMockUser());
expect(result.current.session).toBeDefined();
});
});
it('handles sign in', async () => {
const { result } = renderHook(() => useAuth());
await waitFor(() => {
expect(result.current.loading).toBe(false);
});
const { signIn } = result.current;
const signInResult = await signIn('[email protected]', 'password');
expect(signInResult.error).toBeUndefined();
expect(signInResult.data).toBeDefined();
});
it('handles sign in errors', async () => {
// Mock sign in error
server.use(
rest.post('/api/auth/signin', (req, res, ctx) => {
return res(
ctx.status(401),
ctx.json({ error: 'Invalid credentials' })
);
})
);
const { result } = renderHook(() => useAuth());
await waitFor(() => {
expect(result.current.loading).toBe(false);
});
const { signIn } = result.current;
const signInResult = await signIn('[email protected]', 'wrong-password');
expect(signInResult.error).toBe('Invalid credentials');
});
it('handles sign out', async () => {
const { result } = renderHook(() => useAuth());
await waitFor(() => {
expect(result.current.loading).toBe(false);
});
const { signOut } = result.current;
await signOut();
expect(result.current.user).toBeNull();
expect(result.current.session).toBeNull();
});
});
Create test/api/posts.test.ts:
import { createClient } from '@/lib/supabase/client';
import { createMockPost } from '../utils';
import { server } from '../mocks/server';
// Mock Supabase client
vi.mock('@/lib/supabase/client', () => ({
createClient: vi.fn(() => ({
from: vi.fn(() => ({
select: vi.fn(() => ({
data: [createMockPost()],
error: null,
})),
insert: vi.fn(() => ({
data: createMockPost(),
error: null,
})),
update: vi.fn(() => ({
data: createMockPost(),
error: null,
})),
delete: vi.fn(() => ({
data: createMockPost(),
error: null,
})),
})),
})),
}));
describe('Posts API', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('fetches posts successfully', async () => {
const supabase = createClient();
const { data, error } = await supabase
.from('posts')
.select('*');
expect(data).toEqual([createMockPost()]);
expect(error).toBeNull();
});
it('creates a new post', async () => {
const supabase = createClient();
const newPost = {
title: 'New Post',
content: 'New content',
published: true,
};
const { data, error } = await supabase
.from('posts')
.insert(newPost)
.select()
.single();
expect(data).toEqual(createMockPost(newPost));
expect(error).toBeNull();
});
it('handles API errors', async () => {
// Mock error response
server.use(
rest.get('/api/posts', (req, res, ctx) => {
return res(
ctx.status(500),
ctx.json({ error: 'Internal server error' })
);
})
);
const response = await fetch('/api/posts');
expect(response.status).toBe(500);
});
});
8. Create Test Scripts
Create scripts/test-runner.js:
#!/usr/bin/env node
const { execSync } = require('child_process');
const fs = require('fs');
// ANSI color codes
const colors = {
reset: '\x1b[0m',
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
cyan: '\x1b[36c',
magenta: '\x1b[35m',
};
function colorLog(message, color = 'reset') {
console.log(`${colors[color]}${message}${colors.reset}`);
}
function runCommand(command, description) {
try {
colorLog(`🔍 Running: ${description}`, 'blue');
execSync(command, { stdio: 'inherit' });
colorLog(`✅ ${description} - PASSED`, 'green');
return true;
} catch (error) {
colorLog(`❌ ${description} - FAILED`, 'red');
return false;
}
}
function main() {
const args = process.argv.slice(2);
const testType = args[0] || 'all';
colorLog('🧪 Running Test Suite', 'magenta');
colorLog('====================', 'magenta');
let success = true;
switch (testType) {
case 'unit':
success = runCommand('npm run test:unit', 'Unit tests');
break;
case 'integration':
success = runCommand('npm run test:integration', 'Integration tests');
break;
case 'e2e':
success = runCommand('npm run test:e2e', 'End-to-end tests');
break;
case 'coverage':
success = runCommand('npm run test:coverage', 'Test coverage');
break;
case 'watch':
success = runCommand('npm run test:watch', 'Watch mode');
break;
case 'ci':
success = runCommand('npm run test:ci', 'CI tests');
break;
case 'all':
default:
success = runCommand('npm run test', 'All tests');
break;
}
colorLog('\n📊 Test Summary', 'magenta');
colorLog('================', 'magenta');
if (success) {
colorLog('✅ All tests passed!', 'green');
process.exit(0);
} else {
colorLog('❌ Some tests failed!', 'red');
process.exit(1);
}
}
if (require.main === module) {
main();
}
module.exports = { runCommand };
9. Update Package.json Scripts
Update package.json scripts:
{
"scripts": {
"test": "vitest",
"test:unit": "vitest run --config vitest.config.unit.ts",
"test:integration": "vitest run --config vitest.config.integration.ts",
"test:e2e": "playwright test",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"test:ui": "vitest --ui",
"test:ci": "vitest run --coverage --reporter=junit --outputFile=./test-results/junit.xml",
"test:debug": "vitest --inspect-brk --no-coverage",
"test:runner": "node scripts/test-runner.js",
"test:clean": "rimraf coverage test-results"
}
}
10. Create Test Configuration Files
Create vitest.config.unit.ts:
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'jsdom',
include: ['**/__tests__/**/*.unit.{test,spec}.{js,ts,jsx,tsx}'],
exclude: ['**/integration/**', '**/e2e/**'],
coverage: {
include: ['src/**/*.{js,ts,jsx,tsx}'],
exclude: ['**/*.d.ts', '**/stories/**'],
},
},
});
Create vitest.config.integration.ts:
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'jsdom',
include: ['**/__tests__/**/*.integration.{test,spec}.{js,ts,jsx,tsx}'],
exclude: ['**/unit/**', '**/e2e/**'],
setupFiles: ['./test/integration-setup.ts'],
coverage: {
include: ['src/**/*.{js,ts,jsx,tsx}'],
exclude: ['**/*.d.ts', '**/stories/**'],
},
},
});
Code Examples
Running Tests
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverage
# Run tests with UI
npm run test:ui
# Run specific test types
npm run test:unit
npm run test:integration
npm run test:e2e
# Run tests in CI mode
npm run test:ci
# Debug tests
npm run test:debug
Writing Component Tests
// test/components/Card.test.tsx
import { render, screen } from '@testing-library/react';
import { Card } from '@/components/ui/card';
describe('Card Component', () => {
it('renders with title and content', () => {
render(
<Card>
<CardHeader>
<CardTitle>Test Title</CardTitle>
</CardHeader>
<CardContent>
<p>Test content</p>
</CardContent>
</Card>
);
expect(screen.getByText('Test Title')).toBeInTheDocument();
expect(screen.getByText('Test content')).toBeInTheDocument();
});
});
Writing Hook Tests
// test/hooks/useCounter.test.ts
import { renderHook, act } from '@testing-library/react';
import { useCounter } from '@/hooks/useCounter';
describe('useCounter Hook', () => {
it('initializes with default value', () => {
const { result } = renderHook(() => useCounter());
expect(result.current.count).toBe(0);
});
it('increments count', () => {
const { result } = renderHook(() => useCounter());
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
});
Writing API Tests
// test/api/users.test.ts
import { createServerClient } from '@/lib/supabase/server';
import { server } from '../mocks/server';
describe('Users API', () => {
it('fetches user profile', async () => {
const supabase = createServerClient();
const { data, error } = await supabase
.from('profiles')
.select('*')
.eq('user_id', 'user-1')
.single();
expect(data).toBeDefined();
expect(error).toBeNull();
});
});
Configuration Templates
Complete vitest.config.ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import { resolve } from 'path';
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
setupFiles: ['./test/setup.ts'],
globals: true,
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
thresholds: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
},
},
resolve: {
alias: {
'@': resolve(__dirname, './src'),
},
},
});
Best Practices
- Use Vitest for new projects - Faster and more modern
- Mock external dependencies - Isolate unit tests
- Use MSW for API mocking - Realistic API simulation
- Write descriptive tests - Clear test purpose
- Use proper test structure - Arrange, Act, Assert
- Maintain good coverage - 80% minimum threshold
- Test components and hooks - Full coverage
- Use test utilities - Reduce boilerplate code
Stop Conditions
STOP and report if:
- Test runner installation fails
- Configuration errors occur
- Mock setup fails
- Test execution errors
Expected Outcomes:
- Test runner configured correctly
- Mock server functional
- Test utilities working
- Coverage reporting active
- CI integration working
Verification Checklist
- Test runner installed and configured
- Mock server set up correctly
- Test utilities created
- Example tests functional
- Coverage reporting working
- Package.json scripts updated
- CI configuration working
- Test runner scripts functional
- All test types configured
- Mock data generators working
Version: 1.0.0 Last Updated: 2026-01-31 Skill Category: Architecture - CI
