TDD & Qualité

Tests Fonctionnels : Guide Pratique pour Valider le Comportement de Votre Application

Jérémie Calos

Jérémie Calos

27 janvier 2025

Tests Fonctionnels : Guide Pratique pour Valider le Comportement de Votre Application

Introduction : Tester que Ça Fonctionne, Vraiment

Salut les codeurs ! Aujourd'hui, on va parler des tests fonctionnels. Vous savez, ces tests qui vérifient que votre application fait vraiment ce qu'elle est censée faire, du point de vue de l'utilisateur.

Si les tests unitaires vérifient que chaque pièce fonctionne individuellement, et les tests d'intégration vérifient que les pièces s'assemblent correctement, les tests fonctionnels vérifient que le résultat final correspond à ce que l'utilisateur attend.

C'est comme tester une voiture : vous pouvez tester chaque pièce individuellement, mais au final, ce qui compte, c'est que la voiture roule et vous emmène où vous voulez aller.

Qu'est-ce qu'un Test Fonctionnel ?

Un test fonctionnel vérifie que votre application se comporte comme prévu du point de vue de l'utilisateur final. Il teste une fonctionnalité complète, de bout en bout, sans se soucier de l'implémentation technique.

Contrairement aux tests unitaires qui testent le code, les tests fonctionnels testent le comportement. Ils répondent à la question : "Est-ce que l'application fait ce qu'elle doit faire ?"

Les Différents Types de Tests Fonctionnels

Tests de Récgression

Les tests de régression vérifient que les nouvelles fonctionnalités n'ont pas cassé les anciennes. C'est comme vérifier que la nouvelle peinture n'a pas abîmé les murs existants.

Tests de Fumée (Smoke Tests)

Les tests de fumée sont des tests rapides qui vérifient que les fonctionnalités principales fonctionnent. C'est comme allumer une voiture pour voir si elle démarre.

Tests de Sanité (Sanity Tests)

Les tests de sanité vérifient qu'une fonctionnalité spécifique fonctionne après une modification. C'est plus ciblé que les tests de fumée.

Tests d'Acceptation

Les tests d'acceptation vérifient que l'application répond aux exigences métier. C'est souvent fait avec le client ou le product owner.

Exemples Pratiques

Test Fonctionnel d'une API REST

Voici un exemple de test fonctionnel pour une API de gestion d'utilisateurs :

describe('User API Functional Tests', () => {
  it('should create a user and return it', async () => {
    const userData = {
      name: 'Jérémie',
      email: 'jeremie@example.com',
      password: 'securePassword123'
    };
    
    const response = await request(app)
      .post('/api/users')
      .send(userData)
      .expect(201);
    
    expect(response.body).toMatchObject({
      id: expect.any(String),
      name: 'Jérémie',
      email: 'jeremie@example.com'
    });
    
    // Vérifier que le mot de passe n'est pas retourné
    expect(response.body.password).toBeUndefined();
  });
  
  it('should not create a user with duplicate email', async () => {
    const userData = {
      name: 'Jérémie',
      email: 'jeremie@example.com',
      password: 'securePassword123'
    };
    
    // Créer le premier utilisateur
    await request(app)
      .post('/api/users')
      .send(userData)
      .expect(201);
    
    // Essayer de créer un deuxième utilisateur avec le même email
    await request(app)
      .post('/api/users')
      .send(userData)
      .expect(409); // Conflict
  });
  
  it('should authenticate a user with correct credentials', async () => {
    // Créer un utilisateur
    const userData = {
      name: 'Jérémie',
      email: 'jeremie@example.com',
      password: 'securePassword123'
    };
    
    await request(app)
      .post('/api/users')
      .send(userData)
      .expect(201);
    
    // S'authentifier
    const authResponse = await request(app)
      .post('/api/auth/login')
      .send({
        email: 'jeremie@example.com',
        password: 'securePassword123'
      })
      .expect(200);
    
    expect(authResponse.body).toHaveProperty('token');
  });
});

Test Fonctionnel d'une Interface Utilisateur

Voici un exemple avec Cypress pour tester une interface utilisateur :

describe('User Registration Flow', () => {
  it('should register a new user successfully', () => {
    cy.visit('/register');
    
    // Remplir le formulaire
    cy.get('[data-testid="name-input"]').type('Jérémie');
    cy.get('[data-testid="email-input"]').type('jeremie@example.com');
    cy.get('[data-testid="password-input"]').type('securePassword123');
    cy.get('[data-testid="confirm-password-input"]').type('securePassword123');
    
    // Soumettre le formulaire
    cy.get('[data-testid="submit-button"]').click();
    
    // Vérifier la redirection
    cy.url().should('include', '/dashboard');
    
    // Vérifier le message de succès
    cy.get('[data-testid="success-message"]').should('contain', 'Bienvenue');
  });
  
  it('should show error for invalid email', () => {
    cy.visit('/register');
    
    cy.get('[data-testid="email-input"]').type('invalid-email');
    cy.get('[data-testid="submit-button"]').click();
    
    cy.get('[data-testid="email-error"]').should('be.visible');
  });
});

Les Outils Populaires

Pour les Tests API

  • Supertest (Node.js) : Parfait pour tester les APIs Express
  • REST Assured (Java) : Le standard pour tester les APIs REST
  • Postman/Newman : Excellent pour les tests d'API avec collection

Pour les Tests d'Interface Utilisateur

  • Cypress : Moderne, puissant, avec une excellente documentation
  • Playwright : Supporte plusieurs navigateurs, très performant
  • Selenium : Le vétéran, toujours utile
  • Puppeteer : Contrôle Chrome/Chromium programmatiquement

Les Bonnes Pratiques

1. Testez du Point de Vue de l'Utilisateur

Les tests fonctionnels doivent tester ce que l'utilisateur voit et fait, pas l'implémentation technique. Si l'utilisateur ne voit pas quelque chose, ne le testez pas dans un test fonctionnel.

2. Un Test = Une Fonctionnalité

Chaque test doit tester une fonctionnalité complète. Évitez de tester plusieurs fonctionnalités dans un seul test.

3. Utilisez des Données Réalistes

Utilisez des données qui ressemblent à ce que vous auriez en production. Ça rend les tests plus fiables.

4. Testez les Cas d'Erreur

N'oubliez pas de tester les cas d'erreur. Que se passe-t-il si l'utilisateur entre des données invalides ? Si le serveur est inaccessible ?

5. Gardez les Tests Indépendants

Chaque test doit pouvoir s'exécuter seul, dans n'importe quel ordre. Utilisez des fixtures ou nettoyez les données entre chaque test.

Tests Fonctionnels vs Tests Unitaires

Aspect Tests Fonctionnels Tests Unitaires
Scope Fonctionnalité complète Composant isolé
Point de vue Utilisateur Développeur
Vitesse Plus lents Très rapides
Maintenance Plus difficile Plus facile
Détection de bugs Bugs fonctionnels Bugs dans le code

Les Pièges à Éviter

Le Piège du Test Trop Détaillé

Un test fonctionnel ne doit pas tester chaque détail d'implémentation. Testez le comportement, pas l'implémentation.

Le Piège de la Dépendance Entre Tests

Si vos tests dépendent les uns des autres, vous allez avoir des problèmes. Chaque test doit être indépendant.

Le Piège du Test Fragile

Un test fragile casse même quand le code fonctionne. Souvent, c'est parce que le test dépend de détails d'implémentation qui changent.

Conclusion : Tester le Comportement, Pas le Code

Les tests fonctionnels sont essentiels pour garantir que votre application fait ce qu'elle doit faire du point de vue de l'utilisateur. Ils complètent les tests unitaires et d'intégration en vérifiant le résultat final.

Comme pour les autres types de tests, l'important c'est de trouver le bon équilibre. Trop de tests fonctionnels et vos tests deviennent lents et difficiles à maintenir. Pas assez et vous risquez de manquer des bugs fonctionnels.

Mon conseil ? Commencez par tester les fonctionnalités critiques, celles qui sont au cœur de votre application. Puis, au fur et à mesure, ajoutez des tests pour les fonctionnalités secondaires.

Et n'oubliez pas : un test fonctionnel qui échoue, c'est souvent un bug réel que l'utilisateur aurait rencontré. C'est mieux de le découvrir maintenant, non ?

Allez, maintenant c'est à vous de jouer ! Prenez une fonctionnalité de votre application, et écrivez un test fonctionnel pour elle. Vous verrez, une fois que vous aurez pris le pli, vous ne pourrez plus vous en passer.