Maison >interface Web >js tutoriel >Tests unitaires pour NodeJS à l'aide de Mocha et 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.
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.
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é.
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.
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
Ajoutez les scripts suivants à votre package.json :
{ "scripts": { "test": "NODE_ENV=test mocha --timeout 10000 --exit", "test:coverage": "nyc npm test" } }
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.
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
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 }
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
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
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 });
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 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
{ "scripts": { "test": "NODE_ENV=test mocha --timeout 10000 --exit", "test:coverage": "nyc npm test" } }
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
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 }
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
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
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 });
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 }); }); });
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'); }); });
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); }); });
const mongoose = require('mongoose'); before(async () => { await mongoose.connect(process.env.MONGODB_URI_TEST); }); after(async () => { await mongoose.connection.dropDatabase(); await mongoose.connection.close(); });
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; }); });
La rédaction de cas de tests manuels pour le moka avec chai, bien qu'efficace, présente souvent plusieurs défis :
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.
Difficile à maintenir : à mesure que votre application évolue, la mise à jour et la maintenance des tests manuels deviennent plus complexes et sujettes aux erreurs.
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.
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.
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.
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]
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.
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.
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.
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
Tests liés aux groupes utilisant des blocs describe()
Utilisez des noms de tests descriptifs qui indiquent clairement le comportement attendu
Suivez le modèle AAA (Arrange-Act-Assert) dans chaque test
Gardez les tests atomiques et indépendants
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" } }
'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.
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!