Testing Strategy
Comprehensive testing approach for reliability.
Test Types​
Unit Tests​
- Component testing
- Utility functions
- Business logic
- Isolated modules
Integration Tests​
- API endpoints
- Database operations
- Authentication flows
- External services
E2E Tests​
- User journeys
- Critical paths
- Cross-browser
- Mobile testing
Testing Tools​
Jest​
// jest.config.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/tests/setup.ts'],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/$1'
}
}
React Testing Library​
import { render, screen, fireEvent } from '@testing-library/react'
import { Button } from '@/components/ui/button'
describe('Button', () => {
it('handles click events', () => {
const handleClick = jest.fn()
render(
<Button onClick={handleClick}>
Click me
</Button>
)
fireEvent.click(screen.getByText('Click me'))
expect(handleClick).toHaveBeenCalledTimes(1)
})
})
Component Testing​
Basic Component Test​
import { render, screen } from '@testing-library/react'
import { UserCard } from '@/components/UserCard'
describe('UserCard', () => {
const mockUser = {
id: '1',
name: 'John Doe',
email: 'john@example.com',
role: 'instructor'
}
it('displays user information', () => {
render(<UserCard user={mockUser} />)
expect(screen.getByText('John Doe')).toBeInTheDocument()
expect(screen.getByText('john@example.com')).toBeInTheDocument()
expect(screen.getByText('Instructor')).toBeInTheDocument()
})
})
Testing Hooks​
import { renderHook, act } from "@testing-library/react";
import { useCounter } from "@/hooks/useCounter";
describe("useCounter", () => {
it("increments counter", () => {
const { result } = renderHook(() => useCounter());
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
});
API Testing​
Server Action Tests​
import { createSession } from "@/actions/sessions";
import { prisma } from "@/lib/db";
jest.mock("@/lib/db", () => ({
prisma: {
session: {
create: jest.fn(),
},
},
}));
describe("createSession", () => {
it("creates a new session", async () => {
const mockSession = { id: "1", title: "Test Session" };
prisma.session.create.mockResolvedValue(mockSession);
const result = await createSession({
title: "Test Session",
startTime: new Date(),
});
expect(result).toEqual(mockSession);
});
});
Database Testing​
Test Database Setup​
// tests/helpers/db.ts
import { PrismaClient } from "@prisma/client";
const testDb = new PrismaClient({
datasources: {
db: {
url: process.env.DATABASE_TEST_URL,
},
},
});
beforeEach(async () => {
await testDb.$executeRaw`TRUNCATE TABLE "User" CASCADE`;
});
afterAll(async () => {
await testDb.$disconnect();
});
Mocking​
Mock Providers​
// tests/providers.tsx
export function TestProviders({ children }: { children: React.ReactNode }) {
return (
<ClerkProvider>
<QueryClient>
{children}
</QueryClient>
</ClerkProvider>
)
}
Mock Data​
// tests/mocks/users.ts
export const mockUsers = [
{
id: "1",
clerkId: "clerk_1",
email: "admin@test.com",
role: "admin",
},
{
id: "2",
clerkId: "clerk_2",
email: "instructor@test.com",
role: "instructor",
},
];
Running Tests​
Commands​
# Run all tests
pnpm test
# Watch mode
pnpm test:watch
# Coverage report
pnpm test:coverage
# Specific file
pnpm test UserCard.test.tsx
CI/CD Integration​
# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
- run: pnpm install
- run: pnpm test:ci