Heim  >  Artikel  >  Web-Frontend  >  Unit-Tests für NodeJS mit Mocha und Chai

Unit-Tests für NodeJS mit Mocha und Chai

Patricia Arquette
Patricia ArquetteOriginal
2024-10-26 06:58:03625Durchsuche

Unit testing for NodeJS using Mocha and Chai
Unit-Tests sind wichtig, da sie kleine Teile des Codes überprüfen, um sicherzustellen, dass sie richtig funktionieren, und Fehler frühzeitig finden. Es ist wichtig, diese Tests durchzuführen, bevor eine App veröffentlicht wird. In diesem Leitfaden werden Unit-Tests mit Mocha und Chai behandelt.

Warum Mokka und Chai?

Mocha ist ein funktionsreiches JavaScript-Testframework, das auf Node.js läuft und asynchrone Tests einfach und angenehm macht. Es bietet Funktionen, die in einer bestimmten Reihenfolge ausgeführt werden, Testergebnisse sammeln und genaue Berichte liefern.

Chai ist eine BDD/TDD-Assertionsbibliothek, die mit jedem JavaScript-Test-Framework verwendet werden kann. Es bietet mehrere Schnittstellen, sodass Entwickler den Behauptungsstil wählen können, den sie am bequemsten finden.

Vorteile der Verwendung von Mokka und Chai

  1. Lesbare und aussagekräftige Aussagen

    Chai bietet verschiedene Assertionsstile und Syntaxoptionen, die gut mit Mocha funktionieren, sodass Sie den Stil auswählen können, der Ihren Anforderungen an Klarheit und Lesbarkeit entspricht.

  2. Unterstützung für asynchrone Tests

    Mocha handhabt asynchrone Tests problemlos, sodass Sie asynchronen Code in Node.js-Anwendungen testen können, ohne dass zusätzliche Bibliotheken oder komplexe Setups erforderlich sind.

Einrichten der Testumgebung

Abhängigkeiten installieren

Zuerst richten wir ein neues Node.js-Projekt ein und installieren die notwendigen Abhängigkeiten:

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

Richten Sie Testskripte ein

Fügen Sie die folgenden Skripte zu Ihrem package.json hinzu:

{
  "scripts": {
    "test": "NODE_ENV=test mocha --timeout 10000 --exit",
    "test:coverage": "nyc npm test"
  }
}

Projektübersicht

Bevor wir uns mit dem Testen befassen, wollen wir uns mit der Anwendung befassen, die wir testen werden. Wir erstellen eine einfache, aber sichere Authentifizierungs-API mit den folgenden Funktionen.

Anwendungsstruktur

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

API-Endpunkte

  1. Authentifizierungsendpunkte:
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 }
  1. Benutzerendpunkte:
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

Umgebungseinrichtung zum Testen

Erstellen Sie eine .env.test-Datei für die testspezifische Konfiguration:

PORT=3001
MONGODB_URI=mongodb://localhost:27017/auth-api-test
JWT_SECRET=your-test-secret-key

Teststruktur verstehen

Lassen Sie uns unsere erste Testdatei test/auth.test.js erstellen

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
});

Testen Sie Lifecycle-Hooks

Mocha bietet mehrere Hooks für den Testaufbau und die Bereinigung:

  • before(): Wird einmal vor allen Tests ausgeführt

  • after(): Wird nach allen Tests einmal ausgeführt

  • beforeEach(): Wird vor jedem Test ausgeführt

  • afterEach(): Wird nach jedem Test ausgeführt

Mochas write()- und it()-Blöcke

Tests werden mithilfe von beschreiben()-Blöcken zum Gruppieren verwandter Tests und it()-Blöcken für einzelne Testfälle organisiert:

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

Schreiben unserer ersten Testsuite

Testen der Registrierung und Anmeldung

{
  "scripts": {
    "test": "NODE_ENV=test mocha --timeout 10000 --exit",
    "test:coverage": "nyc npm test"
  }
}

Authentifizierung testen

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

Datenbanktests

Testdatenbank einrichten

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 }

Testen von CRUD-Operationen

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

Best Practices

Behalten Sie Test Atomic bei

  • Jeder Test sollte für sich allein stehen und nicht von anderen Tests abhängig sein

  • Tests sollten in beliebiger Reihenfolge ausgeführt werden können

  • Verwenden Sie die Hooks „before“, „after“, „beforeEach“ und „afterEach“ für die ordnungsgemäße Einrichtung und Bereinigung

    PORT=3001
    MONGODB_URI=mongodb://localhost:27017/auth-api-test
    JWT_SECRET=your-test-secret-key
    

    Folgen Sie dem AAA-Muster

    • Anordnen: Testdaten und -bedingungen einrichten
    • Aktion: Den zu testenden Code ausführen
    • Bestätigen: Überprüfen Sie die Ergebnisse
    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
    });
    

    Testen Sie Edge-Fälle

    Testen Sie Randbedingungen, Fehlerszenarien, ungültige Eingaben und leere oder Nullwerte.

    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
        });
      });
    });
    

    Verwenden Sie beschreibende Testnamen

    Testnamen sollten das getestete Szenario klar beschreiben, einer konsistenten Namenskonvention folgen und das erwartete Verhalten enthalten.

    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');
      });
    });
    

    Scheinen Sie externe Abhängigkeiten

    • Verwenden Sie Stubs und Mocks für externe Dienste
    • Isolieren Sie den zu testenden Code
    • Kontrolltestumgebung
    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);
      });
    });
    

    Gehen Sie mit Versprechen richtig um

    • Zusagen immer zurückgeben oder async/await verwenden
    • Verwenden Sie die richtige Fehlerbehandlung
    • Testen Sie sowohl Erfolgs- als auch Misserfolgsfälle
    const mongoose = require('mongoose');
    
    before(async () => {
      await mongoose.connect(process.env.MONGODB_URI_TEST);
    });
    
    after(async () => {
      await mongoose.connection.dropDatabase();
      await mongoose.connection.close();
    });
    

    Legen Sie geeignete Zeitüberschreitungen fest

    • Legen Sie realistische Zeitüberschreitungen für asynchrone Vorgänge fest
    • Globale Timeouts in der Mocha-Konfiguration konfigurieren
    • Überschreiben Sie bei Bedarf Zeitüberschreitungen für bestimmte Tests
    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;
      });
    });
    

Wir stellen vor: Keploy Unit Test Generator

Das Schreiben manueller Testfälle für Mokka mit Chai ist zwar effektiv, birgt jedoch oft mehrere Herausforderungen:

  1. Zeitaufwändig: Die manuelle Erstellung detaillierter Testsuiten kann viel Zeit in Anspruch nehmen, insbesondere bei großen Codebasen.

  2. Schwierig zu warten: Wenn sich Ihre Anwendung ändert, wird die Aktualisierung und Wartung manueller Tests komplexer und fehleranfälliger.

  3. Inkonsistente Abdeckung: Entwickler konzentrieren sich möglicherweise auf die Hauptpfade, fehlende Randfälle oder Fehlerszenarien, die Fehler in der Produktion verursachen könnten.

  4. Fähigkeitsabhängig: Die Qualität und Wirksamkeit manueller Tests hängen stark von den Testfähigkeiten des Entwicklers und seiner Vertrautheit mit der Codebasis ab.

  5. Repetitiv und mühsam: Das Schreiben ähnlicher Teststrukturen für mehrere Komponenten oder Funktionen kann langweilig sein und möglicherweise zu weniger Liebe zum Detail führen.

  6. Verzögertes Feedback: Die zum Schreiben manueller Tests benötigte Zeit kann die Entwicklung verlangsamen und wichtige Rückmeldungen zur Codequalität und -funktionalität verzögern.

Um diese Probleme anzugehen, hat Keploy ein ut-gen eingeführt, das KI nutzt, um den Testprozess zu automatisieren und zu verbessern. Dies ist die erste Implementierung des Meta LLM-Forschungspapiers, das Codesemantik versteht und aussagekräftige Unit-Tests erstellt.

Ziel ist es, die Unit-Test-Generierung (UTG) durch die schnelle Erstellung gründlicher Unit-Tests zu automatisieren. Dadurch wird der Bedarf an repetitiver manueller Arbeit reduziert, Randfälle verbessert, indem die Tests erweitert werden, um komplexere Szenarien abzudecken, die häufig manuell übersehen werden, und die Testabdeckung erhöht, um eine vollständige Abdeckung sicherzustellen, wenn die Codebasis wächst.

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

Hauptfunktionen

  • Automatisierte Unit-Test-Generierung (UTG): Generieren Sie schnell umfassende Unit-Tests und reduzieren Sie redundanten manuellen Aufwand.

  • Randfälle verbessern: Erweitern und verbessern Sie den Testumfang, um komplexere Szenarien abzudecken, die häufig manuell übersehen werden.

  • Erhöhung der Testabdeckung: Mit zunehmender Codebasis wird es möglich, eine umfassende Abdeckung sicherzustellen.

Abschluss

Zusammenfassend lässt sich sagen, dass die Beherrschung des Node.js-Backend-Tests mit Mocha und Chai wichtig für Entwickler ist, die möchten, dass ihre Anwendungen zuverlässig und stark sind. Durch die Verwendung des Test-Frameworks von Mocha und der Clear-Assertion-Bibliothek von Chai können Entwickler detaillierte Testsuiten erstellen, die viele Teile ihres Codes abdecken, von einfachen Unit-Tests bis hin zu komplexen asynchronen Vorgängen. Das Befolgen von Best Practices wie die Fokussierung der Tests, die Verwendung klarer Namen und der korrekte Umgang mit Versprechen kann Ihren Testprozess erheblich verbessern. Durch den Einsatz dieser Tools und Techniken in Ihrem Entwicklungsworkflow können Sie Fehler frühzeitig finden, die Codequalität verbessern und sicherere und effizientere Anwendungen bereitstellen.

FAQs

Was ist der Unterschied zwischen Mokka und Chai und brauche ich beides?

Obwohl es sich bei beiden um Testwerkzeuge handelt, dienen sie unterschiedlichen Zwecken. Mocha ist ein Test-Framework, das die Struktur zum Organisieren und Ausführen von Tests bereitstellt (mithilfe der Blöcke „beschreiben()“ und „it()“). Chai ist eine Assertionsbibliothek, die Funktionen zum Überprüfen von Ergebnissen bereitstellt (wie „expect()“, „should“ und „assertion“). Während Sie Mokka ohne Chai verwenden können, erhalten Sie durch die gemeinsame Verwendung eine umfassendere und ausdrucksstärkere Testlösung.

Wie richte ich Testdaten zwischen Tests ein und bereinige sie?

Mocha bietet mehrere Lebenszyklus-Hooks für die Verwaltung von Testdaten:

  • before(): Vor allen Tests einmal ausführen

  • beforeEach(): Vor jedem Test ausführen

  • afterEach(): Nach jedem Test ausführen

  • after(): Nach allen Tests einmal ausführen

Beispiel:

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

Wie sollte ich meine Tests für eine bessere Organisation und Wartung strukturieren?

  1. Gruppieren Sie verwandte Tests mithilfe von beschreiben()-Blöcken

  2. Verwenden Sie beschreibende Testnamen, die das erwartete Verhalten klar zum Ausdruck bringen

  3. Folgen Sie bei jedem Test dem AAA-Muster (Arrange-Act-Assert)

  4. Tests atomar und unabhängig halten

  5. Testdateien so organisieren, dass sie Ihre Quellcodestruktur widerspiegeln

Beispiel:

{
  "scripts": {
    "test": "NODE_ENV=test mocha --timeout 10000 --exit",
    "test:coverage": "nyc npm test"
  }
}

Was ist der Unterschied zwischen before, beforeEach, after und afterEach?

„before“ wird einmal vor allen Tests ausgeführt, „beforeEach“ wird vor jedem einzelnen Test ausgeführt, „afterEach“ wird nach jedem einzelnen Test ausgeführt und „after“ wird einmal ausgeführt, nachdem alle Tests abgeschlossen sind. Diese sind nützlich zum Einrichten und Bereinigen von Testdaten.

Wie teste ich asynchronen Code?

Sie können in Ihren Tests Async/Await- oder Return-Promises verwenden. Fügen Sie einfach „async“ vor Ihrer Testfunktion hinzu und verwenden Sie „await“, wenn Sie asynchrone Vorgänge aufrufen. Stellen Sie sicher, dass Sie für längere Vorgänge geeignete Zeitüberschreitungen festlegen.

Das obige ist der detaillierte Inhalt vonUnit-Tests für NodeJS mit Mocha und Chai. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn