Claude Code Mocha: Tests, Hooks und Chai-Assertions mit KI
Mocha ist eines der flexibelsten Test-Frameworks im JavaScript-Ökosystem — und genau diese Flexibilität ist seine größte Hürde. Kein eigenes Assertion-System, keine eingebauten Mocking-Tools, keine Coverage-Integration out of the box. Stattdessen: Entscheidungen. Chai oder Node assert? Sinon oder testdouble? nyc oder c8? Und wie verbindet man das alles sinnvoll?
Claude Code Mocha bedeutet: diesen Konfigurationsaufwand einmalig mit KI aufzusetzen und dann systematisch Tests zu schreiben, die wirklich etwas aussagen. Dieser Artikel zeigt, wie das konkret funktioniert — von der .mocharc.yml bis zu async Tests mit Nock-Mocking und Coverage-Reports in CI.
Claude Code Mastery — Testing, Agents, Hooks auf Deutsch
Nicht nur Mocha: der Kurs zeigt, wie du Claude Code wirklich produktiv einsetzt — für JavaScript-Testing, autonome Agents und professionelle Workflows. Einmalig bezahlt, kein Abo.
Zum Kurs — Jetzt starten → Einmalzahlung · Kein Abo · 14 Tage Rückgaberecht1. Mocha konfigurieren mit Claude Code
Der erste Schritt bei jedem neuen Projekt: eine .mocharc.yml die zu deiner Projektstruktur passt. Claude Code liest dein Verzeichnis, erkennt die Ordnerstruktur und schlägt eine sinnvolle Konfiguration vor:
claude "Erstelle eine .mocharc.yml für dieses Projekt. Tests liegen in
test/, Quellcode in src/. Wir nutzen ES Modules, Timeout 5000ms,
parallele Ausführung soll möglich sein."
Claude Code erzeugt dann eine Konfiguration wie diese — und erklärt dabei jeden Parameter:
# .mocharc.yml
spec: 'test/**/*.spec.js'
timeout: 5000
reporter: spec
require:
- './test/setup.js'
parallel: false
exit: true
ES Modules und Mocha: Bei "type": "module" in der package.json muss Mocha mit --experimental-specifier-resolution=node oder einem entsprechenden Loader gestartet werden. Frag Claude Code direkt: "Wie konfiguriere ich Mocha für ES Modules in Node 20?" — es kennt die aktuelle Empfehlung und schreibt das passende npm-Script dazu.
Dazu passt ein package.json-Script das Claude Code ebenfalls generiert:
{
"scripts": {
"test": "mocha",
"test:watch": "mocha --watch",
"test:coverage": "nyc mocha"
}
}
2. describe, it und die Lifecycle-Hooks
Mocha organisiert Tests in describe-Blöcke und einzelne Test-Cases in it-Blöcke. Dazu kommen vier Lifecycle-Hooks: before, after, beforeEach und afterEach. Claude Code ist besonders hilfreich beim Entscheiden, welcher Hook wo hingehört — ein Bereich, der in größeren Test-Suites schnell unübersichtlich wird.
import { expect } from 'chai';
import { UserService } from '../src/user-service.js';
import { createTestDb, destroyTestDb } from './helpers/db.js';
describe('UserService', () => {
let db;
let service;
// Einmal vor allen Tests: DB aufbauen
before(async () => {
db = await createTestDb();
});
// Einmal nach allen Tests: DB aufräumen
after(async () => {
await destroyTestDb(db);
});
// Vor jedem Test: saubere Service-Instanz
beforeEach(() => {
service = new UserService(db);
});
// Nach jedem Test: Test-Daten löschen
afterEach(async () => {
await db.collection('users').deleteMany({});
});
describe('#createUser()', () => {
it('sollte einen neuen Nutzer anlegen', async () => {
const user = await service.createUser({
name: 'Max Mustermann',
email: 'max@example.com',
});
expect(user).to.have.property('id');
expect(user.email).to.equal('max@example.com');
});
it('sollte bei doppelter E-Mail einen Fehler werfen', async () => {
await service.createUser({ name: 'Max', email: 'max@example.com' });
await expect(
service.createUser({ name: 'Max 2', email: 'max@example.com' })
).to.be.rejectedWith('E-Mail bereits vergeben');
});
});
});
Claude Code hilft dabei, diese Struktur für dein konkretes Modul zu generieren. Übergib einfach die Quelldatei:
claude "Schreib eine Mocha-Test-Suite für src/user-service.js mit
before/after für die DB-Verbindung und beforeEach/afterEach für
Test-Isolation. Nutze Chai expect."
3. Chai-Assertions: expect, assert und should
Chai bietet drei Assertion-Stile. Claude Code kennt alle drei und wählt auf Nachfrage den passenden aus — oder erklärt die Unterschiede wenn du noch nicht entschieden hast.
expect-Stil (empfohlen)
import { expect } from 'chai';
// Grundlegende Assertions
expect(result).to.equal(42);
expect(user).to.deep.equal({ id: 1, name: 'Max' });
expect(items).to.have.lengthOf(3);
expect(user).to.have.property('email').that.includes('@');
// Negation
expect(result).to.not.be.null;
expect(list).to.not.be.empty;
// Typen
expect(value).to.be.a('string');
expect(fn).to.be.a('function');
// Fehler
expect(() => riskyFn()).to.throw(TypeError, 'ungültiger Parameter');
// Promises (mit chai-as-promised)
await expect(asyncFn()).to.be.fulfilled;
await expect(asyncFn()).to.be.rejectedWith('Fehler');
assert-Stil
import { assert } from 'chai';
assert.equal(result, 42);
assert.deepEqual(user, { id: 1, name: 'Max' });
assert.isNull(value);
assert.throws(() => riskyFn(), TypeError);
chai-as-promised nicht vergessen: Für async Assertions mit rejectedWith und fulfilled brauchst du das Plugin chai-as-promised. Installieren mit npm install --save-dev chai-as-promised und in der Setup-Datei registrieren: chai.use(chaiAsPromised). Claude Code richtet das auf Wunsch vollständig ein.
4. Async-Tests: done, Promises und async/await
Mocha unterstützt drei Wege für asynchrone Tests. Claude Code erklärt auf Anfrage die Unterschiede und migriert veralteten Code automatisch auf async/await:
done-Callback (veraltet, aber noch in alten Codebases)
it('sollte Daten laden', function (done) {
fetchData('endpoint', (err, data) => {
if (err) return done(err);
expect(data).to.have.property('items');
done();
});
});
Promise-Rückgabe
it('sollte Nutzer finden', () => {
return userRepo.findById(1).then((user) => {
expect(user.name).to.equal('Max');
});
});
async/await (empfohlen)
it('sollte Nutzer finden', async () => {
const user = await userRepo.findById(1);
expect(user.name).to.equal('Max');
});
it('sollte bei fehlendem Nutzer einen Fehler werfen', async () => {
await expect(userRepo.findById(9999))
.to.be.rejectedWith('Nutzer nicht gefunden');
});
Um bestehende done-Tests zu migrieren, genügt ein Befehl:
claude "Migriere alle done-Callback-Tests in test/legacy/ auf async/await.
Behalte die Testlogik identisch, ändere nur den async-Stil."
5. Sinon: Stubs und Spies
Sinon ist das Standard-Mocking-Tool für Mocha. Claude Code kennt die Sinon-API genau und erstellt Stubs für externe Abhängigkeiten — besonders nützlich bei Datenbankaufrufen, API-Requests oder Drittanbieter-SDKs.
import sinon from 'sinon';
import { expect } from 'chai';
import { OrderService } from '../src/order-service.js';
import * as emailModule from '../src/email.js';
describe('OrderService', () => {
let sendEmailStub;
beforeEach(() => {
// Stub: ersetzt die echte Funktion vollständig
sendEmailStub = sinon.stub(emailModule, 'sendEmail').resolves({ sent: true });
});
afterEach(() => {
// Stubs nach jedem Test zurücksetzen!
sinon.restore();
});
it('sollte eine Bestätigungsmail senden', async () => {
const service = new OrderService();
await service.placeOrder({ productId: 42, quantity: 1 });
// Spy-Assertions: wurde die Funktion aufgerufen?
expect(sendEmailStub.calledOnce).to.be.true;
expect(sendEmailStub.firstCall.args[0]).to.include({
subject: 'Bestellbestätigung',
});
});
it('sollte bei Mail-Fehler trotzdem die Bestellung speichern', async () => {
sendEmailStub.rejects(new Error('SMTP nicht erreichbar'));
const service = new OrderService();
const order = await service.placeOrder({ productId: 42, quantity: 1 });
expect(order.status).to.equal('confirmed');
// Mail-Fehler darf Bestellung nicht blockieren
});
});
Spies überwachen echte Funktionen ohne sie zu ersetzen — gut für Logging, Event-Emitter oder interne Aufrufe:
import sinon from 'sinon';
const spy = sinon.spy(console, 'warn');
// ... Code ausführen ...
expect(spy.calledWith('Deprecated')).to.be.true;
sinon.restore();
6. Nock für HTTP-Mocking
Wenn dein Code HTTP-Requests macht — gegen eine REST-API, einen Webhook-Endpunkt oder einen externen Dienst — mockt Nock diese Requests auf Node-Ebene, ohne dass echter Netzwerkverkehr entsteht.
import nock from 'nock';
import { expect } from 'chai';
import { WeatherClient } from '../src/weather-client.js';
describe('WeatherClient', () => {
afterEach(() => {
nock.cleanAll();
});
it('sollte aktuelles Wetter abrufen', async () => {
// HTTP-Request abfangen und Antwort definieren
nock('https://api.weather.example')
.get('/current')
.query({ city: 'Berlin', apiKey: 'test-key' })
.reply(200, {
city: 'Berlin',
temp: 18,
condition: 'partly_cloudy',
});
const client = new WeatherClient({ apiKey: 'test-key' });
const weather = await client.getCurrent('Berlin');
expect(weather.temp).to.equal(18);
expect(weather.condition).to.equal('partly_cloudy');
});
it('sollte bei API-Fehler einen aussagekräftigen Fehler werfen', async () => {
nock('https://api.weather.example')
.get('/current')
.query({ city: 'Berlin', apiKey: 'test-key' })
.reply(503, { error: 'Service Unavailable' });
const client = new WeatherClient({ apiKey: 'test-key' });
await expect(client.getCurrent('Berlin'))
.to.be.rejectedWith('Wetterdienst nicht erreichbar');
});
});
API-Keys in Tests: Echte Credentials gehören nie in Testdateien. Nutze Umgebungsvariablen: process.env.WEATHER_API_KEY oder einen Test-Dummy-Wert wie oben. Nock fängt die Requests ab, bevor sie das Netzwerk erreichen — der echte Key wird nie gesendet.
7. nyc und Istanbul: Coverage messen
nyc ist der Coverage-Reporter für Mocha, basierend auf Istanbul. Claude Code richtet die Konfiguration ein und erklärt, welche Coverage-Schwellen sinnvoll sind:
# .nycrc.json
{
"include": ["src/**/*.js"],
"exclude": ["src/**/*.spec.js", "src/generated/**"],
"reporter": ["text", "html", "lcov"],
"branches": 80,
"lines": 85,
"functions": 80,
"statements": 85,
"check-coverage": true
}
Mit dieser Konfiguration schlägt npm run test:coverage fehl, wenn die Coverage-Schwellen nicht erreicht werden — ein hartes Gate in CI. Das Kommando:
nyc mocha
Gibt aus, wo Zweige nicht getestet sind:
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 87.50 | 75.00 | 83.33 | 87.50 |
order.js | 87.50 | 75.00 | 83.33 | 87.50 | 45-52
----------|---------|----------|---------|---------|-------------------
Für die Zeilen ohne Coverage fragt Claude Code direkt:
claude "src/order.js Zeilen 45-52 sind nicht durch Tests abgedeckt.
Schreib einen Test-Case der diesen Zweig ausführt."
8. Integration in npm test und CI/CD
Ein vollständiges Test-Setup in der package.json:
{
"scripts": {
"test": "mocha",
"test:watch": "mocha --watch",
"test:coverage": "nyc mocha",
"test:ci": "nyc mocha --reporter min --exit"
}
}
Und eine GitHub Actions Workflow-Datei, die Claude Code auf Wunsch generiert:
# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run test:ci
- name: Coverage-Report hochladen
uses: actions/upload-artifact@v4
if: always()
with:
name: coverage
path: coverage/
9. Der Claude Code Workflow beim Testen
Das eigentliche Potenzial von Claude Code im Test-Workflow liegt nicht im Schreiben einzelner Tests — es liegt darin, systematisch eine vollständige Test-Suite aufzubauen. Der typische Ablauf:
Schritt 1: Analyse der bestehenden Codebase. Claude Code liest alle Quelldateien und listet auf, welche Module noch keine Tests haben, welche Edge Cases fehlen und welche Integrationspunkte besonders kritisch sind.
claude "Analysiere src/ und erstelle eine Liste aller untesteten
Funktionen. Priorisiere nach Risiko (externe Abhängigkeiten,
Fehlerbehandlung, komplexe Logik)."
Schritt 2: Tests generieren, nicht diktieren. Statt jeden Test manuell zu schreiben, lässt du Claude Code die Basis generieren und reviewst dann:
claude "Schreib vollständige Mocha-Tests für src/payment-processor.js.
Decke Happy Path, Fehlerfälle und Edge Cases ab. Nutze Sinon
für externe Aufrufe und Nock für HTTP."
Schritt 3: Roten Test zuerst. Bei TDD: erst den Test beschreiben, dann die Implementierung. Claude Code schreibt den fehlschlagenden Test — du implementierst die Funktion, bis der Test grün ist.
claude "Schreib einen Mocha-Test für eine noch nicht existierende Funktion
validateIban(iban: string): boolean. Der Test soll zuerst rot sein."
"Tests schreiben ist der Teil der Entwicklung, den die meisten Entwickler aufschieben — weil er zeitaufwändig ist ohne sofort sichtbares Ergebnis. Claude Code macht den Aufwand kleiner, nicht die Tests unwichtiger."
Schritt 4: Coverage-Lücken gezielt schließen. Nach dem ersten Coverage-Run zeigt nyc genau, welche Zeilen fehlen. Claude Code liest den Report und schreibt die fehlenden Tests:
npm run test:coverage 2>&1 | claude "Welche Tests fehlen laut
Coverage-Report? Schreib sie für die Dateien mit der niedrigsten
Branch-Coverage."
Zwei verwandte Artikel die auf diesem Thema aufbauen:
- Claude Code Jest — dasselbe Vorgehen für Jest-Projekte mit integriertem Mocking
- Claude Code TDD — Test-Driven Development mit Claude Code als Pair-Programmierer
Claude Code Mastery — von Mocha bis zum produktiven Agenten
Testing ist eine Stärke von Claude Code — aber nicht die einzige. Im Kurs lernst du Agents, MCP-Server, Hooks, Multi-Agent-Workflows und mehr. Vollständig auf Deutsch, einmalig bezahlt.
Jetzt starten → Einmalzahlung · Kein Abo · 14 Tage RückgaberechtKurs · Claude Code Mastery
Von Mocha-Tests zum produktiven AI-Agenten
Testing. Agents. MCP. Hooks. Multi-Agent-Workflows. Alles auf Deutsch, einmalig bezahlt — kein Abo, keine Plattformabhängigkeit.
Einmalzahlung · Kein Abo · 14 Tage Rückgaberecht