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.
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
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.
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
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" } }
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
Points de terminaison de l'API
- 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 }
- 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
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
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 });
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
É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" } }
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
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 }
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
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 connexionCopier après la connexionSuivez 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 connexionCopier après la connexionTester 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 connexionUtiliser 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 connexionDé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 connexionGé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 connexionDé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 :
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]
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
Comment structurer mes tests pour une meilleure organisation et maintenance ?
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" } }
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!

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

Video Face Swap
Échangez les visages dans n'importe quelle vidéo sans effort grâce à notre outil d'échange de visage AI entièrement gratuit !

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Sujets chauds











Python convient plus aux débutants, avec une courbe d'apprentissage en douceur et une syntaxe concise; JavaScript convient au développement frontal, avec une courbe d'apprentissage abrupte et une syntaxe flexible. 1. La syntaxe Python est intuitive et adaptée à la science des données et au développement back-end. 2. JavaScript est flexible et largement utilisé dans la programmation frontale et côté serveur.

Le passage de C / C à JavaScript nécessite de s'adapter à la frappe dynamique, à la collecte des ordures et à la programmation asynchrone. 1) C / C est un langage dactylographié statiquement qui nécessite une gestion manuelle de la mémoire, tandis que JavaScript est dynamiquement typé et que la collecte des déchets est automatiquement traitée. 2) C / C doit être compilé en code machine, tandis que JavaScript est une langue interprétée. 3) JavaScript introduit des concepts tels que les fermetures, les chaînes de prototypes et la promesse, ce qui améliore la flexibilité et les capacités de programmation asynchrones.

Les principales utilisations de JavaScript dans le développement Web incluent l'interaction client, la vérification du formulaire et la communication asynchrone. 1) Mise à jour du contenu dynamique et interaction utilisateur via les opérations DOM; 2) La vérification du client est effectuée avant que l'utilisateur ne soumette les données pour améliorer l'expérience utilisateur; 3) La communication de rafraîchissement avec le serveur est réalisée via la technologie AJAX.

L'application de JavaScript dans le monde réel comprend un développement frontal et back-end. 1) Afficher les applications frontales en créant une application de liste TODO, impliquant les opérations DOM et le traitement des événements. 2) Construisez RestulAPI via Node.js et Express pour démontrer les applications back-end.

Comprendre le fonctionnement du moteur JavaScript en interne est important pour les développeurs car il aide à écrire du code plus efficace et à comprendre les goulots d'étranglement des performances et les stratégies d'optimisation. 1) Le flux de travail du moteur comprend trois étapes: analyse, compilation et exécution; 2) Pendant le processus d'exécution, le moteur effectuera une optimisation dynamique, comme le cache en ligne et les classes cachées; 3) Les meilleures pratiques comprennent l'évitement des variables globales, l'optimisation des boucles, l'utilisation de const et de locations et d'éviter une utilisation excessive des fermetures.

Python et JavaScript ont leurs propres avantages et inconvénients en termes de communauté, de bibliothèques et de ressources. 1) La communauté Python est amicale et adaptée aux débutants, mais les ressources de développement frontal ne sont pas aussi riches que JavaScript. 2) Python est puissant dans les bibliothèques de science des données et d'apprentissage automatique, tandis que JavaScript est meilleur dans les bibliothèques et les cadres de développement frontaux. 3) Les deux ont des ressources d'apprentissage riches, mais Python convient pour commencer par des documents officiels, tandis que JavaScript est meilleur avec MDNWEBDOCS. Le choix doit être basé sur les besoins du projet et les intérêts personnels.

Les choix de Python et JavaScript dans les environnements de développement sont importants. 1) L'environnement de développement de Python comprend Pycharm, Jupyternotebook et Anaconda, qui conviennent à la science des données et au prototypage rapide. 2) L'environnement de développement de JavaScript comprend Node.js, VScode et WebPack, qui conviennent au développement frontal et back-end. Le choix des bons outils en fonction des besoins du projet peut améliorer l'efficacité du développement et le taux de réussite du projet.

C et C jouent un rôle essentiel dans le moteur JavaScript, principalement utilisé pour implémenter des interprètes et des compilateurs JIT. 1) C est utilisé pour analyser le code source JavaScript et générer une arborescence de syntaxe abstraite. 2) C est responsable de la génération et de l'exécution de bytecode. 3) C met en œuvre le compilateur JIT, optimise et compile le code de point chaud à l'exécution et améliore considérablement l'efficacité d'exécution de JavaScript.
