Maison > interface Web > js tutoriel > le corps du texte

Tests unitaires pour NodeJS à l'aide de Mocha et Chai

Patricia Arquette
Libérer: 2024-10-26 06:58:03
original
624 Les gens l'ont consulté

Unit testing for NodeJS using Mocha and Chai
Les tests unitaires sont importants car ils vérifient de petites parties de code pour s'assurer qu'elles fonctionnent correctement et détectent les bogues rapidement. Il est important de faire ces tests avant de publier une application. Ce guide couvrira les tests unitaires avec Mocha et Chai.

Pourquoi Moka et Chai ?

Mocha est un framework de test JavaScript riche en fonctionnalités qui s'exécute sur Node.js, rendant les tests asynchrones simples et agréables. Il fournit des fonctions qui s'exécutent dans un ordre spécifique, collectant les résultats des tests et offrant des rapports précis.

Chai est une bibliothèque d'assertions BDD/TDD qui peut être utilisée avec n'importe quel framework de test JavaScript. Il propose plusieurs interfaces, permettant aux développeurs de choisir le style d'assertion qu'ils trouvent le plus confortable.

Avantages de l'utilisation du Moka et du Chai

  1. Affirmations lisibles et expressives

    Chai propose différents styles d'assertion et options de syntaxe qui fonctionnent bien avec Mocha, vous permettant de choisir le style qui convient à vos besoins de clarté et de lisibilité.

  2. Prise en charge des tests asynchrones

    Mocha gère facilement les tests asynchrones, vous permettant de tester du code asynchrone dans les applications Node.js sans avoir besoin de bibliothèques supplémentaires ou de configurations complexes.

Configuration de l'environnement de test

Installation des dépendances

Tout d'abord, configurons un nouveau projet Node.js et installons les dépendances nécessaires :

mkdir auth-api-testing
cd auth-api-testing
npm init -y

# Install production dependencies
npm install express jsonwebtoken mongoose bcryptjs dotenv

# Install development dependencies
npm install --save-dev mocha chai chai-http supertest nyc
Copier après la connexion
Copier après la connexion
Copier après la connexion

Configurer des scripts de test

Ajoutez les scripts suivants à votre package.json :

{
  "scripts": {
    "test": "NODE_ENV=test mocha --timeout 10000 --exit",
    "test:coverage": "nyc npm test"
  }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion

Aperçu du projet

Avant de plonger dans les tests, comprenons l'application que nous allons tester. Nous construisons une API d'authentification simple mais sécurisée avec les fonctionnalités suivantes.

Structure des candidatures

src/
├── models/
│   └── user.model.js       # User schema and model
├── routes/
│   ├── auth.routes.js      # Authentication routes
│   └── user.routes.js      # User management routes
├── middleware/
│   ├── auth.middleware.js  # JWT verification middleware
│   └── validate.js         # Request validation middleware
├── controllers/
│   ├── auth.controller.js  # Authentication logic
│   └── user.controller.js  # User management logic
└── app.js                  # Express application setup
Copier après la connexion
Copier après la connexion

Points de terminaison de l'API

  1. Points de terminaison d’authentification :
POST /api/auth/register
- Registers new user
- Accepts: { email, password, name }
- Returns: { token, user }

POST /api/auth/login
- Authenticates existing user
- Accepts: { email, password }
- Returns: { token, user }
Copier après la connexion
Copier après la connexion
  1. Points de terminaison utilisateur :
GET /api/users/profile
- Gets current user profile
- Requires: JWT Authentication
- Returns: User object

PUT /api/users/profile
- Updates user profile
- Requires: JWT Authentication
- Accepts: { name, email }
- Returns: Updated user object
Copier après la connexion
Copier après la connexion

Configuration de l'environnement pour les tests

Créez un fichier .env.test pour une configuration spécifique au test :

PORT=3001
MONGODB_URI=mongodb://localhost:27017/auth-api-test
JWT_SECRET=your-test-secret-key
Copier après la connexion
Copier après la connexion

Comprendre la structure des tests

Créons notre premier fichier de test test/auth.test.js

const chai = require('chai');
const chaiHttp = require('chai-http');
const app = require('../src/app');
const User = require('../src/models/user.model');

chai.use(chaiHttp);
const expect = chai.expect;

describe('Auth API Tests', () => {
  // Runs before all tests
  before(async () => {
    await User.deleteMany({});
  });

  // Runs after each test
  afterEach(async () => {
    await User.deleteMany({});
  });

  // Test suites will go here
});
Copier après la connexion
Copier après la connexion

Tester les crochets du cycle de vie

Mocha fournit plusieurs hooks pour la configuration et le nettoyage des tests :

  • before() : s'exécute une fois avant tous les tests

  • after() : s'exécute une fois après tous les tests

  • beforeEach() : s'exécute avant chaque test

  • afterEach() : s'exécute après chaque test

Les blocs décrire() et it() de Mocha

Les tests sont organisés à l'aide de blocs décrire() pour regrouper les tests associés et de blocs it() pour les cas de test individuels :

mkdir auth-api-testing
cd auth-api-testing
npm init -y

# Install production dependencies
npm install express jsonwebtoken mongoose bcryptjs dotenv

# Install development dependencies
npm install --save-dev mocha chai chai-http supertest nyc
Copier après la connexion
Copier après la connexion
Copier après la connexion

Écrire notre première suite de tests

Test d'inscription et de connexion

{
  "scripts": {
    "test": "NODE_ENV=test mocha --timeout 10000 --exit",
    "test:coverage": "nyc npm test"
  }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion

Tester l'authentification

src/
├── models/
│   └── user.model.js       # User schema and model
├── routes/
│   ├── auth.routes.js      # Authentication routes
│   └── user.routes.js      # User management routes
├── middleware/
│   ├── auth.middleware.js  # JWT verification middleware
│   └── validate.js         # Request validation middleware
├── controllers/
│   ├── auth.controller.js  # Authentication logic
│   └── user.controller.js  # User management logic
└── app.js                  # Express application setup
Copier après la connexion
Copier après la connexion

Test de base de données

Configuration de la base de données de tests

POST /api/auth/register
- Registers new user
- Accepts: { email, password, name }
- Returns: { token, user }

POST /api/auth/login
- Authenticates existing user
- Accepts: { email, password }
- Returns: { token, user }
Copier après la connexion
Copier après la connexion

Test des opérations CRUD

GET /api/users/profile
- Gets current user profile
- Requires: JWT Authentication
- Returns: User object

PUT /api/users/profile
- Updates user profile
- Requires: JWT Authentication
- Accepts: { name, email }
- Returns: Updated user object
Copier après la connexion
Copier après la connexion

Meilleures pratiques

Gardez le test atomique

  • Chaque test doit être autonome et ne pas dépendre d'autres tests

  • Les tests devraient pouvoir s'exécuter dans n'importe quel ordre

  • Utilisez les hooks before, after, beforeEach et afterEach pour une configuration et un nettoyage appropriés

    PORT=3001
    MONGODB_URI=mongodb://localhost:27017/auth-api-test
    JWT_SECRET=your-test-secret-key
    
    Copier après la connexion
    Copier après la connexion

    Suivez le modèle AAA

    • Organiser : configurer les données et les conditions de test
    • Act : Exécuter le code en cours de test
    • Assert : Vérifiez les résultats
    const chai = require('chai');
    const chaiHttp = require('chai-http');
    const app = require('../src/app');
    const User = require('../src/models/user.model');
    
    chai.use(chaiHttp);
    const expect = chai.expect;
    
    describe('Auth API Tests', () => {
      // Runs before all tests
      before(async () => {
        await User.deleteMany({});
      });
    
      // Runs after each test
      afterEach(async () => {
        await User.deleteMany({});
      });
    
      // Test suites will go here
    });
    
    Copier après la connexion
    Copier après la connexion

    Tester les cas Edge

    Testez les conditions aux limites, les scénarios d'erreur, les entrées non valides et les valeurs vides ou nulles.

    describe('Auth API Tests', () => {
      describe('POST /api/auth/register', () => {
        it('should register a new user successfully', async () => {
          // Test implementation
        });
    
        it('should return error when email already exists', async () => {
          // Test implementation
        });
      });
    });
    
    Copier après la connexion

    Utiliser des noms de tests descriptifs

    Les noms des tests doivent décrire clairement le scénario testé, suivre une convention de dénomination cohérente et inclure le comportement attendu.

    describe('POST /api/auth/register', () => {
      it('should register a new user successfully', async () => {
        const res = await chai
          .request(app)
          .post('/api/auth/register')
          .send({
            email: 'test@example.com',
            password: 'Password123!',
            name: 'Test User'
          });
    
        expect(res).to.have.status(201);
        expect(res.body).to.have.property('token');
        expect(res.body).to.have.property('user');
        expect(res.body.user).to.have.property('email', 'test@example.com');
      });
    
      it('should return 400 when email already exists', async () => {
        // First create a user
        await chai
          .request(app)
          .post('/api/auth/register')
          .send({
            email: 'test@example.com',
            password: 'Password123!',
            name: 'Test User'
          });
    
        // Try to create another user with same email
        const res = await chai
          .request(app)
          .post('/api/auth/register')
          .send({
            email: 'test@example.com',
            password: 'Password123!',
            name: 'Test User 2'
          });
    
        expect(res).to.have.status(400);
        expect(res.body).to.have.property('error');
      });
    });
    
    Copier après la connexion

    Dépendances externes simulées

    • Utiliser des stubs et des mocks pour les services externes
    • Isoler le code en cours de test
    • Environnement de test de contrôle
    describe('Protected Routes', () => {
      let token;
      let userId;
    
      beforeEach(async () => {
        // Create a test user and get token
        const res = await chai
          .request(app)
          .post('/api/auth/register')
          .send({
            email: 'test@example.com',
            password: 'Password123!',
            name: 'Test User'
          });
    
        token = res.body.token;
        userId = res.body.user._id;
      });
    
      it('should get user profile with valid token', async () => {
        const res = await chai
          .request(app)
          .get('/api/users/profile')
          .set('Authorization', `Bearer ${token}`);
    
        expect(res).to.have.status(200);
        expect(res.body).to.have.property('email', 'test@example.com');
      });
    
      it('should return 401 with invalid token', async () => {
        const res = await chai
          .request(app)
          .get('/api/users/profile')
          .set('Authorization', 'Bearer invalid-token');
    
        expect(res).to.have.status(401);
      });
    });
    
    Copier après la connexion

    Gérer correctement les promesses

    • Renvoyez toujours les promesses ou utilisez async/await
    • Utilisez une gestion appropriée des erreurs
    • Testez les cas de réussite et d'échec
    const mongoose = require('mongoose');
    
    before(async () => {
      await mongoose.connect(process.env.MONGODB_URI_TEST);
    });
    
    after(async () => {
      await mongoose.connection.dropDatabase();
      await mongoose.connection.close();
    });
    
    Copier après la connexion

    Définir des délais d'attente appropriés

    • Définissez des délais d'attente réalistes pour les opérations asynchrones
    • Configurer les délais d'attente globaux dans la configuration moka
    • Remplacer les délais d'attente pour des tests spécifiques si nécessaire
    describe('User CRUD Operations', () => {
      it('should update user profile', async () => {
        const res = await chai
          .request(app)
          .put(`/api/users/${userId}`)
          .set('Authorization', `Bearer ${token}`)
          .send({
            name: 'Updated Name'
          });
    
        expect(res).to.have.status(200);
        expect(res.body).to.have.property('name', 'Updated Name');
      });
    
      it('should delete user account', async () => {
        const res = await chai
          .request(app)
          .delete(`/api/users/${userId}`)
          .set('Authorization', `Bearer ${token}`);
    
        expect(res).to.have.status(200);
    
        // Verify user is deleted
        const user = await User.findById(userId);
        expect(user).to.be.null;
      });
    });
    
    Copier après la connexion

Présentation du générateur de tests unitaires Keploy

La rédaction de cas de tests manuels pour le moka avec chai, bien qu'efficace, présente souvent plusieurs défis :

  1. Prend du temps : Créer manuellement des suites de tests détaillées peut prendre beaucoup de temps, en particulier pour les bases de code volumineuses.

  2. Difficile à maintenir : à mesure que votre application évolue, la mise à jour et la maintenance des tests manuels deviennent plus complexes et sujettes aux erreurs.

  3. Couverture incohérente : les développeurs peuvent se concentrer sur les chemins principaux, les cas extrêmes manquants ou les scénarios d'erreur qui pourraient provoquer des bugs en production.

  4. Dépend des compétences : La qualité et l'efficacité des tests manuels dépendent fortement des compétences de test du développeur et de sa familiarité avec la base de code.

  5. Répétitif et fastidieux : Écrire des structures de test similaires pour plusieurs composants ou fonctions peut être ennuyeux, conduisant éventuellement à moins d'attention aux détails.

  6. Retours retardés : le temps nécessaire à l'écriture de tests manuels peut ralentir le développement, retardant ainsi les retours importants sur la qualité et la fonctionnalité du code.

Pour résoudre ces problèmes, Keploy a introduit une génération ut qui utilise l'IA pour automatiser et améliorer le processus de test, ce qui constitue la première implémentation du document de recherche Meta LLM qui comprend la sémantique du code et crée des tests unitaires significatifs.

Il vise à automatiser la génération de tests unitaires (UTG) en produisant rapidement des tests unitaires approfondis. Cela réduit le besoin de travail manuel répétitif, améliore les cas extrêmes en étendant les tests pour couvrir des scénarios plus complexes souvent manqués manuellement et augmente la couverture des tests pour garantir une couverture complète à mesure que la base de code se développe.

%[https://marketplace.visualstudio.com/items?itemName=Keploy.keployio]

Fonctionnalités clés

  • Automatiser la génération de tests unitaires (UTG) : Générez rapidement des tests unitaires complets et réduisez les efforts manuels redondants.

  • Améliorez les cas extrêmes : Étendez et améliorez la portée des tests pour couvrir des scénarios plus complexes qui sont souvent manqués manuellement.

  • Améliorez la couverture des tests : À mesure que la base de code se développe, garantir une couverture exhaustive devient réalisable.

Conclusion

En conclusion, maîtriser les tests backend Node.js avec Mocha et Chai est important pour les développeurs qui souhaitent que leurs applications soient fiables et solides. En utilisant le cadre de test de Mocha et la bibliothèque d'assertions claires de Chai, les développeurs peuvent créer des suites de tests détaillées couvrant de nombreuses parties de leur code, des simples tests unitaires aux opérations asynchrones complexes. Suivre les meilleures pratiques telles que garder les tests ciblés, utiliser des noms clairs et gérer correctement les promesses peut considérablement améliorer votre processus de test. En utilisant ces outils et techniques dans votre flux de travail de développement, vous pouvez détecter les bogues plus tôt, améliorer la qualité du code et fournir des applications plus sécurisées et plus efficaces.

FAQ

Quelle est la différence entre Moka et Chai, et ai-je besoin des deux ?

Bien que les deux soient des outils de test, ils servent à des fins différentes. Mocha est un framework de test qui fournit la structure permettant d'organiser et d'exécuter des tests (à l'aide des blocs décrire() et it()). Chai est une bibliothèque d'assertions qui fournit des fonctions pour vérifier les résultats (comme expect(), Should et Assert). Bien que vous puissiez utiliser Mocha sans Chai, leur utilisation ensemble vous offre une solution de test plus complète et plus expressive.

Comment configurer et nettoyer les données de test entre les tests ?

Mocha fournit plusieurs hooks de cycle de vie pour gérer les données de test :

  • before() : exécuter une fois avant tous les tests

  • beforeEach() : exécuter avant chaque test

  • afterEach() : Exécuter après chaque test

  • after() : exécuter une fois après tous les tests

Exemple :

mkdir auth-api-testing
cd auth-api-testing
npm init -y

# Install production dependencies
npm install express jsonwebtoken mongoose bcryptjs dotenv

# Install development dependencies
npm install --save-dev mocha chai chai-http supertest nyc
Copier après la connexion
Copier après la connexion
Copier après la connexion

Comment structurer mes tests pour une meilleure organisation et maintenance ?

  1. Tests liés aux groupes utilisant des blocs describe()

  2. Utilisez des noms de tests descriptifs qui indiquent clairement le comportement attendu

  3. Suivez le modèle AAA (Arrange-Act-Assert) dans chaque test

  4. Gardez les tests atomiques et indépendants

  5. Organisez les fichiers de test pour refléter la structure de votre code source

Exemple :

{
  "scripts": {
    "test": "NODE_ENV=test mocha --timeout 10000 --exit",
    "test:coverage": "nyc npm test"
  }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion

Quelle est la différence entre avant, avantEach, après et aprèsEach ?

'before' s'exécute une fois avant tous les tests, 'beforeEach' s'exécute avant chaque test individuel, 'afterEach' s'exécute après chaque test individuel et 'after' s'exécute une fois que tous les tests sont terminés. Ceux-ci sont utiles pour configurer et nettoyer les données de test.

Comment tester le code asynchrone ?

Vous pouvez utiliser async/await ou return promises dans vos tests. Ajoutez simplement « async » avant votre fonction de test et utilisez « wait » lors de l'appel d'opérations asynchrones. Assurez-vous de définir des délais d'attente appropriés pour les opérations plus longues.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Derniers articles par auteur
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!