# 16. Testing Strategy ## Testing Pyramid ``` ┌───────┐ │ E2E │ ← Few, critical paths │ Tests │ ┌┴───────┴┐ │Integration│ ← API & DB tests │ Tests │ ┌┴─────────┴┐ │ Unit │ ← Many, fast │ Tests │ └───────────┘ ``` ## Unit Tests (Vitest) ```typescript // tests/unit/schemas/character.test.ts import { describe, it, expect } from 'vitest'; import { createCharacterSchema } from '@/lib/schemas/character'; describe('createCharacterSchema', () => { it('validates correct input', () => { const input = { name: 'TestChar', level: 200, classId: 1, className: 'Cra', serverId: 1, serverName: 'Imagiro', accountId: 'clx123abc', }; expect(() => createCharacterSchema.parse(input)).not.toThrow(); }); it('rejects invalid level', () => { const input = { name: 'TestChar', level: 250, // Invalid: max is 200 classId: 1, className: 'Cra', serverId: 1, serverName: 'Imagiro', accountId: 'clx123abc', }; expect(() => createCharacterSchema.parse(input)).toThrow(); }); }); ``` ## Integration Tests ```typescript // tests/integration/characters.test.ts import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { db } from '@/lib/server/db'; describe('Character API', () => { let testUserId: string; let testAccountId: string; beforeAll(async () => { // Setup test data const user = await db.user.create({ data: { email: 'test@test.com', passwordHash: 'hash', }, }); testUserId = user.id; const account = await db.account.create({ data: { name: 'TestAccount', userId: testUserId, }, }); testAccountId = account.id; }); afterAll(async () => { // Cleanup await db.user.delete({ where: { id: testUserId } }); }); it('creates a character', async () => { const character = await db.character.create({ data: { name: 'TestChar', level: 200, classId: 1, className: 'Cra', serverId: 1, serverName: 'Imagiro', accountId: testAccountId, }, }); expect(character.name).toBe('TestChar'); expect(character.level).toBe(200); }); }); ``` ## E2E Tests (Playwright) ```typescript // tests/e2e/characters.spec.ts import { test, expect } from '@playwright/test'; test.describe('Characters Page', () => { test.beforeEach(async ({ page }) => { // Login await page.goto('/login'); await page.fill('[name="email"]', 'test@test.com'); await page.fill('[name="password"]', 'password'); await page.click('button[type="submit"]'); await page.waitForURL('/'); }); test('displays character list', async ({ page }) => { await page.goto('/characters'); await expect(page.locator('h1')).toContainText('Personnages'); await expect(page.locator('table')).toBeVisible(); }); test('can create a new character', async ({ page }) => { await page.goto('/characters'); await page.click('button:has-text("Ajouter")'); await page.fill('[name="name"]', 'NewChar'); await page.fill('[name="level"]', '100'); await page.click('button:has-text("Créer")'); await expect(page.locator('text=NewChar')).toBeVisible(); }); }); ``` ---