Files
2025-08-27 13:31:55 +02:00

11 KiB

🧪 Guide des Tests Automatiques - Mes Budgets Participatifs

📋 Vue d'ensemble

Ce guide décrit la suite de tests automatiques complète mise en place pour l'application "Mes Budgets Participatifs". Les tests couvrent toutes les fonctionnalités essentielles et garantissent la qualité et la fiabilité du code.

🎯 Objectifs des Tests

Couverture complète

  • Services : Logique métier et interactions avec la base de données
  • Composants : Interface utilisateur et interactions
  • Hooks : Logique réutilisable et gestion d'état
  • API Routes : Endpoints et validation des données
  • Utilitaires : Fonctions helper et validation
  • Intégration : Flux complets et interactions entre modules
  • End-to-End : Expérience utilisateur complète

Qualité du code

  • Fiabilité : Détection précoce des régressions
  • Maintenabilité : Tests comme documentation vivante
  • Refactoring : Confiance pour les modifications
  • Performance : Validation des optimisations

🏗️ Architecture des Tests

Structure des dossiers

src/__tests__/
├── utils/
│   └── test-utils.tsx          # Utilitaires et mocks communs
├── lib/
│   ├── services.test.ts        # Tests des services
│   ├── auth.test.ts           # Tests d'authentification
│   └── utils.test.ts          # Tests des utilitaires
├── components/
│   ├── AuthGuard.test.tsx     # Tests du composant de protection
│   └── base/
│       └── BaseModal.test.tsx # Tests des composants de base
├── hooks/
│   └── useFormState.test.ts   # Tests des hooks personnalisés
├── api/
│   └── test-smtp.test.ts      # Tests des API routes
├── integration/
│   └── campaign-management.test.tsx # Tests d'intégration
└── e2e/
    └── voting-flow.test.ts    # Tests end-to-end

🧪 Types de Tests

1. Tests Unitaires (Jest + React Testing Library)

Services (src/__tests__/lib/)

// Exemple : Test du service de campagnes
describe('campaignService', () => {
  it('should create a campaign', async () => {
    const result = await campaignService.create(newCampaign);
    expect(result).toEqual(mockCampaign);
  });
});

Fonctionnalités testées :

  • CRUD des campagnes
  • Gestion des participants
  • Gestion des propositions
  • Système de vote
  • Paramètres de l'application
  • Gestion des erreurs

Composants (src/__tests__/components/)

// Exemple : Test du composant AuthGuard
it('should redirect when not authenticated', async () => {
  mockAuthService.isAuthenticated.mockResolvedValue(false);
  render(<AuthGuard><ProtectedContent /></AuthGuard>);
  await waitFor(() => {
    expect(mockRouter.push).toHaveBeenCalledWith('/admin/login');
  });
});

Fonctionnalités testées :

  • Protection des routes
  • Modaux et formulaires
  • Gestion des états
  • Interactions utilisateur
  • Validation des props

Hooks (src/__tests__/hooks/)

// Exemple : Test du hook useFormState
it('should validate form data', () => {
  const { result } = renderHook(() => useFormState(initialData));
  const isValid = result.current.validate(validator);
  expect(isValid).toBe(true);
});

Fonctionnalités testées :

  • Gestion d'état des formulaires
  • Validation synchrone et asynchrone
  • Gestion des erreurs
  • Soumission des formulaires

2. Tests d'Intégration (src/__tests__/integration/)

Gestion des Campagnes

describe('Campaign Management Integration', () => {
  it('should handle complete campaign workflow', async () => {
    // Créer une campagne
    const campaign = await campaignService.create(newCampaign);
    
    // Ajouter des participants
    const participant = await participantService.create(newParticipant);
    
    // Ajouter des propositions
    const proposition = await propositionService.create(newProposition);
    
    // Vérifier l'intégrité des données
    expect(participant.campaign_id).toBe(campaign.id);
    expect(proposition.campaign_id).toBe(campaign.id);
  });
});

Fonctionnalités testées :

  • Workflows complets
  • Intégrité référentielle
  • Gestion des erreurs en cascade
  • Performance des opérations

3. Tests End-to-End (Playwright)

Flux de Vote

test('should complete full voting flow', async ({ page }) => {
  // Naviguer vers la page de vote
  await page.goto('/campaigns/test-campaign-id/vote/test-participant-id');
  
  // Voter sur les propositions
  await page.locator('[data-testid="vote-slider"]').fill('50');
  
  // Soumettre les votes
  await page.click('[data-testid="submit-votes"]');
  
  // Vérifier le succès
  await expect(page.locator('[data-testid="success-message"]')).toBeVisible();
});

Fonctionnalités testées :

  • Expérience utilisateur complète
  • Gestion des erreurs réseau
  • Mode hors ligne
  • Responsive design
  • Accessibilité

🛠️ Configuration

Jest Configuration (package.json)

{
  "jest": {
    "testEnvironment": "jsdom",
    "setupFilesAfterEnv": ["<rootDir>/jest.setup.js"],
    "moduleNameMapping": {
      "^@/(.*)$": "<rootDir>/src/$1"
    },
    "collectCoverageFrom": [
      "src/**/*.{js,jsx,ts,tsx}",
      "!src/**/*.d.ts"
    ],
    "coverageThreshold": {
      "global": {
        "branches": 80,
        "functions": 80,
        "lines": 80,
        "statements": 80
      }
    }
  }
}

Playwright Configuration (playwright.config.ts)

export default defineConfig({
  testDir: './src/__tests__/e2e',
  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
  },
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit', use: { ...devices['Desktop Safari'] } },
    { name: 'Mobile Chrome', use: { ...devices['Pixel 5'] } },
    { name: 'Mobile Safari', use: { ...devices['iPhone 12'] } },
  ],
});

🚀 Commandes de Test

Tests Unitaires et d'Intégration

# Lancer tous les tests
npm test

# Lancer les tests en mode watch
npm run test:watch

# Lancer les tests avec couverture
npm run test:coverage

# Lancer un test spécifique
npm test -- --testNamePattern="campaignService"

Tests End-to-End

# Lancer tous les tests E2E
npm run test:e2e

# Lancer les tests E2E en mode UI
npx playwright test --ui

# Lancer les tests E2E sur un navigateur spécifique
npx playwright test --project=chromium

# Lancer les tests E2E en mode debug
npx playwright test --debug

Tests de Sécurité

# Lancer les tests de sécurité
npm run test:security

📊 Métriques de Qualité

Couverture de Code

  • Objectif : 80% minimum
  • Branches : 80%
  • Fonctions : 80%
  • Lignes : 80%
  • Statements : 80%

Performance des Tests

  • Tests unitaires : < 5 secondes
  • Tests d'intégration : < 30 secondes
  • Tests E2E : < 2 minutes

Fiabilité

  • Taux de succès : > 95%
  • Tests flaky : 0
  • Régressions détectées : 100%

🔧 Mocks et Stubs

Mocks Supabase

jest.mock('@/lib/supabase', () => ({
  supabase: {
    auth: {
      getSession: jest.fn(),
      signInWithPassword: jest.fn(),
      signOut: jest.fn(),
    },
    from: jest.fn(() => ({
      select: jest.fn().mockReturnThis(),
      insert: jest.fn().mockReturnThis(),
      update: jest.fn().mockReturnThis(),
      delete: jest.fn().mockReturnThis(),
      eq: jest.fn().mockReturnThis(),
      single: jest.fn().mockReturnThis(),
      then: jest.fn().mockResolvedValue({ data: null, error: null }),
    })),
  },
}));

Mocks Next.js

jest.mock('next/navigation', () => ({
  useRouter() {
    return {
      push: jest.fn(),
      replace: jest.fn(),
      prefetch: jest.fn(),
      back: jest.fn(),
      forward: jest.fn(),
      refresh: jest.fn(),
    };
  },
  useSearchParams() {
    return new URLSearchParams();
  },
  usePathname() {
    return '/';
  },
}));

🎯 Bonnes Pratiques

Nommage des Tests

// ✅ Bon : Description claire et spécifique
it('should create campaign with valid data', async () => {
  // Test implementation
});

// ❌ Mauvais : Description vague
it('should work', async () => {
  // Test implementation
});

Organisation des Tests

describe('CampaignService', () => {
  describe('create', () => {
    it('should create campaign with valid data', async () => {
      // Test implementation
    });

    it('should reject invalid data', async () => {
      // Test implementation
    });
  });

  describe('update', () => {
    it('should update existing campaign', async () => {
      // Test implementation
    });
  });
});

Gestion des Données de Test

// ✅ Bon : Données de test centralisées
export const mockCampaign = {
  id: 'test-campaign-id',
  title: 'Test Campaign',
  description: 'Test description',
  status: 'deposit' as const,
  budget_per_user: 100,
  spending_tiers: '10,25,50,100',
  slug: 'test-campaign',
  created_at: '2024-01-01T00:00:00Z',
  updated_at: '2024-01-01T00:00:00Z',
};

🚨 Gestion des Erreurs

Tests d'Erreur

it('should handle network errors gracefully', async () => {
  mockCampaignService.getAll.mockRejectedValue(new Error('Network error'));
  
  await expect(campaignService.getAll()).rejects.toThrow('Network error');
});

Tests de Validation

it('should validate required fields', async () => {
  const invalidData = { title: '', description: '' };
  
  const result = validateCampaignData(invalidData);
  
  expect(result.errors.title).toBe('Title is required');
  expect(result.errors.description).toBe('Description is required');
});

📈 Intégration Continue

GitHub Actions

name: Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm ci
      - run: npm test
      - run: npm run test:coverage
      - run: npm run test:e2e

Seuils de Qualité

  • Couverture : 80% minimum
  • Tests E2E : 100% de succès
  • Linting : 0 erreurs
  • Build : Succès obligatoire

🔍 Debugging des Tests

Tests Unitaires

# Debug avec console.log
npm test -- --verbose

# Debug avec debugger
npm test -- --runInBand --no-cache

Tests E2E

# Mode debug interactif
npx playwright test --debug

# Mode UI pour inspection
npx playwright test --ui

# Screenshots et vidéos
npx playwright test --reporter=html

📚 Ressources

Documentation Officielle

Exemples de Code


Cette suite de tests garantit la qualité, la fiabilité et la maintenabilité de l'application "Mes Budgets Participatifs" ! 🚀