Mocha Cheatsheet¶
Mocha ist ein funktionsreiches JavaScript Test-Framework auf Node.js und im Browser, das asynchrone Tests einfach und lustig macht. Mocha-Tests laufen seriell, ermöglichen eine flexible und genaue Berichterstattung, während ungezogene Ausnahmen zu den richtigen Testfällen zu kartieren. < p>
Inhaltsverzeichnis¶
- [Installation](#installation
- (#getting-started)
- [Teststruktur](LINK_2__
- (Hooks)(LINK_3_)
- [Bestimmungen](LINK_4__
- [Async Testing](LINK_5_
- [Konfiguration](#configuration_
- [Reporter](LINK_7__
- (#browser-testing)
- (Mocking)(LINK_9_)
- [Erweiterte Funktionen](#advanced-features
- Plugins
- [CI/CD Integration](LINK_12__
- Beste Praktiken
- (Debugging)(LINK_14_)
- Performance
- (#troubleshooting_)
Installation¶
Einfache Installation¶
# Install Mocha globally
npm install -g mocha
# Install Mocha locally (recommended)
npm install --save-dev mocha
# Install with assertion library
npm install --save-dev mocha chai
# Install with additional tools
npm install --save-dev mocha chai sinon nyc
```_
### Projektaufbau
```bash
# Initialize new project
mkdir my-mocha-project
cd my-mocha-project
npm init -y
# Install dependencies
npm install --save-dev mocha chai
# Create test directory
mkdir test
# Create first test file
touch test/test.js
```_
### Paket.json Konfiguration
```json
{
"scripts": {
"test": "mocha",
"test:watch": "mocha --watch",
"test:coverage": "nyc mocha",
"test:reporter": "mocha --reporter spec",
"test:grep": "mocha --grep 'pattern'",
"test:timeout": "mocha --timeout 5000"
},
"devDependencies": {
"mocha": "^10.0.0",
"chai": "^4.3.0",
"sinon": "^15.0.0",
"nyc": "^15.1.0"
}
}
```_
### Verzeichnis Struktur
## Erste Schritte
### Erster Test
```javascript
// lib/calculator.js
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
function multiply(a, b) {
return a * b;
}
function divide(a, b) {
if (b === 0) {
throw new Error('Division by zero');
}
return a / b;
}
module.exports = { add, subtract, multiply, divide };
```_
```javascript
// test/calculator.test.js
const { expect } = require('chai');
const { add, subtract, multiply, divide } = require('../lib/calculator');
describe('Calculator', function() {
describe('Addition', function() {
it('should add two positive numbers', function() {
expect(add(2, 3)).to.equal(5);
});
it('should add negative numbers', function() {
expect(add(-2, -3)).to.equal(-5);
});
it('should add zero', function() {
expect(add(5, 0)).to.equal(5);
});
});
describe('Division', function() {
it('should divide positive numbers', function() {
expect(divide(10, 2)).to.equal(5);
});
it('should throw error for division by zero', function() {
expect(() => divide(10, 0)).to.throw('Division by zero');
});
});
});
```_
### Laufende Prüfungen
```bash
# Run all tests
npm test
# Run specific test file
npx mocha test/calculator.test.js
# Run tests with pattern
npx mocha test/**/*.test.js
# Run tests with grep
npx mocha --grep "Addition"
# Run tests in watch mode
npx mocha --watch
# Run tests with reporter
npx mocha --reporter json
```_
### Basistest Syntax
```javascript
// Test suite
describe('Feature Name', function() {
// Test case
it('should do something', function() {
// Test implementation
});
// Pending test
it('should do something else');
// Skipped test
it.skip('should skip this test', function() {
// This won't run
});
// Only run this test
it.only('should only run this test', function() {
// Only this test will run
});
});
```_
## Prüfstruktur
### Blocks abschreiben
```javascript
describe('User Management', function() {
describe('User Creation', function() {
it('should create a new user', function() {
// Test implementation
});
it('should validate user data', function() {
// Test implementation
});
});
describe('User Authentication', function() {
it('should authenticate valid user', function() {
// Test implementation
});
it('should reject invalid credentials', function() {
// Test implementation
});
});
});
```_
### Eingebettete Abschriften
```javascript
describe('API', function() {
describe('Users Endpoint', function() {
describe('GET /users', function() {
it('should return all users', function() {
// Test implementation
});
it('should return 200 status', function() {
// Test implementation
});
});
describe('POST /users', function() {
it('should create new user', function() {
// Test implementation
});
it('should return 201 status', function() {
// Test implementation
});
});
});
});
```_
### Prüforganisation
```javascript
// Good: Descriptive test names
describe('Email Validator', function() {
it('should return true for valid email addresses', function() {
// Test implementation
});
it('should return false for invalid email addresses', function() {
// Test implementation
});
it('should handle edge cases like empty strings', function() {
// Test implementation
});
});
// Good: Group related functionality
describe('Shopping Cart', function() {
describe('Adding Items', function() {
it('should add item to cart');
it('should update cart total');
it('should increment item count');
});
describe('Removing Items', function() {
it('should remove item from cart');
it('should update cart total');
it('should decrement item count');
});
});
```_
### Testkontext
```javascript
describe('User Service', function() {
context('when user exists', function() {
it('should return user data', function() {
// Test implementation
});
it('should update user successfully', function() {
// Test implementation
});
});
context('when user does not exist', function() {
it('should return null', function() {
// Test implementation
});
it('should throw error on update', function() {
// Test implementation
});
});
});
```_
## Haken
### Grund Haken
```javascript
describe('Database Tests', function() {
// Runs once before all tests in this describe block
before(function() {
console.log('Setting up database connection');
// Setup code here
});
// Runs once after all tests in this describe block
after(function() {
console.log('Closing database connection');
// Cleanup code here
});
// Runs before each test in this describe block
beforeEach(function() {
console.log('Preparing test data');
// Setup for each test
});
// Runs after each test in this describe block
afterEach(function() {
console.log('Cleaning up test data');
// Cleanup after each test
});
it('should save user', function() {
// Test implementation
});
it('should find user', function() {
// Test implementation
});
});
```_
### Async Haken
```javascript
describe('Async Setup', function() {
before(async function() {
this.timeout(5000); // Increase timeout for setup
// Async setup
this.database = await connectToDatabase();
this.server = await startServer();
});
after(async function() {
// Async cleanup
await this.database.close();
await this.server.stop();
});
beforeEach(async function() {
// Clear database before each test
await this.database.clear();
// Seed test data
await this.database.seed({
users: [
{ name: 'John', email: 'john@example.com' },
{ name: 'Jane', email: 'jane@example.com' }
]
});
});
it('should find users', async function() {
const users = await this.database.findAll('users');
expect(users).to.have.length(2);
});
});
```_
### Hook Erbteil
```javascript
describe('Parent Suite', function() {
before(function() {
console.log('Parent before');
});
beforeEach(function() {
console.log('Parent beforeEach');
});
describe('Child Suite', function() {
before(function() {
console.log('Child before');
});
beforeEach(function() {
console.log('Child beforeEach');
});
it('should inherit hooks', function() {
// Execution order:
// 1. Parent before
// 2. Child before
// 3. Parent beforeEach
// 4. Child beforeEach
// 5. Test execution
});
});
});
```_
### Bedingte Haken
```javascript
describe('Conditional Setup', function() {
before(function() {
if (process.env.NODE_ENV === 'test') {
// Only run in test environment
this.mockServer = startMockServer();
}
});
beforeEach(function() {
// Skip setup for specific tests
if (this.currentTest.title.includes('integration')) {
this.skip();
}
});
it('should run unit test', function() {
// This will run
});
it('should run integration test', function() {
// This will be skipped by beforeEach
});
});
```_
## Beschwörung
### Chai Asser
```javascript
const { expect } = require('chai');
describe('Chai Assertions', function() {
it('should test equality', function() {
expect(2 + 2).to.equal(4);
expect({ name: 'John' }).to.deep.equal({ name: 'John' });
expect([1, 2, 3]).to.deep.equal([1, 2, 3]);
});
it('should test truthiness', function() {
expect(true).to.be.true;
expect(false).to.be.false;
expect(null).to.be.null;
expect(undefined).to.be.undefined;
expect('hello').to.exist;
});
it('should test types', function() {
expect('hello').to.be.a('string');
expect(42).to.be.a('number');
expect([]).to.be.an('array');
expect({}).to.be.an('object');
expect(() => {}).to.be.a('function');
});
it('should test properties', function() {
const obj = { name: 'John', age: 30 };
expect(obj).to.have.property('name');
expect(obj).to.have.property('age', 30);
expect(obj).to.have.all.keys('name', 'age');
});
it('should test arrays', function() {
const arr = [1, 2, 3, 4, 5];
expect(arr).to.have.length(5);
expect(arr).to.include(3);
expect(arr).to.include.members([2, 4]);
expect(arr).to.have.ordered.members([1, 2, 3, 4, 5]);
});
it('should test strings', function() {
expect('hello world').to.contain('world');
expect('hello world').to.match(/^hello/);
expect('hello world').to.have.length(11);
});
});
```_
### Zollanmeldungen
```javascript
const { expect } = require('chai');
// Extend Chai with custom assertion
chai.use(function(chai, utils) {
chai.Assertion.addMethod('validEmail', function() {
const obj = this._obj;
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
this.assert(
emailRegex.test(obj),
'expected #{this} to be a valid email',
'expected #{this} not to be a valid email'
);
});
});
describe('Custom Assertions', function() {
it('should validate email addresses', function() {
expect('user@example.com').to.be.validEmail;
expect('invalid-email').to.not.be.validEmail;
});
});
```_
### Sollten Style Asser
```javascript
const should = require('chai').should();
describe('Should Style', function() {
it('should use should syntax', function() {
const user = { name: 'John', age: 30 };
user.should.be.an('object');
user.should.have.property('name', 'John');
user.age.should.equal(30);
const numbers = [1, 2, 3];
numbers.should.have.length(3);
numbers.should.include(2);
});
});
```_
### Assert Style Asserierung
```javascript
const assert = require('chai').assert;
describe('Assert Style', function() {
it('should use assert syntax', function() {
const user = { name: 'John', age: 30 };
assert.isObject(user);
assert.property(user, 'name');
assert.equal(user.name, 'John');
assert.equal(user.age, 30);
const numbers = [1, 2, 3];
assert.lengthOf(numbers, 3);
assert.include(numbers, 2);
});
});
```_
## Async Testing
### Verfahren
```javascript
describe('Promise Testing', function() {
it('should resolve promise', function() {
return fetchUser(1).then(user => {
expect(user.name).to.equal('John');
});
});
it('should reject promise', function() {
return fetchUser(-1).catch(error => {
expect(error.message).to.equal('User not found');
});
});
it('should use async/await', async function() {
const user = await fetchUser(1);
expect(user.name).to.equal('John');
});
it('should handle async errors', async function() {
try {
await fetchUser(-1);
throw new Error('Should have thrown');
} catch (error) {
expect(error.message).to.equal('User not found');
}
});
});
```_
### Rückrufe
```javascript
describe('Callback Testing', function() {
it('should handle callbacks with done', function(done) {
fetchUserCallback(1, (error, user) => {
if (error) return done(error);
try {
expect(user.name).to.equal('John');
done();
} catch (assertionError) {
done(assertionError);
}
});
});
it('should handle callback errors', function(done) {
fetchUserCallback(-1, (error, user) => {
try {
expect(error).to.exist;
expect(error.message).to.equal('User not found');
expect(user).to.not.exist;
done();
} catch (assertionError) {
done(assertionError);
}
});
});
});
```_
### Timeouts
```javascript
describe('Timeout Testing', function() {
// Set timeout for entire suite
this.timeout(5000);
it('should complete within timeout', function(done) {
// Set timeout for specific test
this.timeout(2000);
setTimeout(() => {
expect(true).to.be.true;
done();
}, 1000);
});
it('should handle slow operations', async function() {
this.timeout(10000);
const result = await slowOperation();
expect(result).to.exist;
});
it('should disable timeout', function(done) {
this.timeout(0); // Disable timeout
// Very slow operation
setTimeout(done, 30000);
});
});
```_
### Retouren
```javascript
describe('Retry Testing', function() {
// Retry failed tests
this.retries(3);
it('should retry flaky test', function() {
// This test might fail randomly
if (Math.random() < 0.7) {
throw new Error('Random failure');
}
expect(true).to.be.true;
});
it('should retry specific test', function() {
this.retries(5);
// Test implementation
});
});
```_
## Konfiguration
### Mocha Konfigurationsdatei
```json
// .mocharc.json
{
"spec": "test/**/*.test.js",
"require": ["test/helpers/setup.js"],
"timeout": 5000,
"reporter": "spec",
"recursive": true,
"exit": true,
"bail": false,
"grep": "",
"invert": false,
"checkLeaks": true,
"globals": ["expect", "sinon"],
"retries": 0,
"slow": 75,
"ui": "bdd"
}
```_
### JavaScript Konfiguration
```javascript
// .mocharc.js
module.exports = {
spec: 'test/**/*.test.js',
require: ['test/helpers/setup.js'],
timeout: 5000,
reporter: 'spec',
recursive: true,
exit: true,
// Environment-specific configuration
...(process.env.NODE_ENV === 'ci' && {
reporter: 'json',
timeout: 10000,
retries: 2
})
};
```_
### Datei einrichten
```javascript
// test/helpers/setup.js
const chai = require('chai');
const sinon = require('sinon');
// Global setup
global.expect = chai.expect;
global.sinon = sinon;
// Chai plugins
chai.use(require('chai-as-promised'));
chai.use(require('sinon-chai'));
// Global hooks
before(function() {
console.log('Global setup');
});
after(function() {
console.log('Global cleanup');
});
beforeEach(function() {
// Reset sinon stubs/spies
sinon.restore();
});
```_
### Umweltvariablen
```javascript
// test/helpers/setup.js
process.env.NODE_ENV = 'test';
process.env.DATABASE_URL = 'mongodb://localhost:27017/test';
process.env.API_URL = 'http://localhost:3001';
// Load environment-specific config
if (process.env.CI) {
// CI-specific setup
process.env.TIMEOUT = '10000';
} else {
// Local development setup
process.env.TIMEOUT = '5000';
}
```_
### Mehrere Konfigurationen
```javascript
// mocha.config.js
const baseConfig = {
timeout: 5000,
recursive: true,
exit: true
};
const configs = {
unit: {
...baseConfig,
spec: 'test/unit/**/*.test.js',
reporter: 'spec'
},
integration: {
...baseConfig,
spec: 'test/integration/**/*.test.js',
timeout: 10000,
reporter: 'json'
},
e2e: {
...baseConfig,
spec: 'test/e2e/**/*.test.js',
timeout: 30000,
reporter: 'tap'
}
};
module.exports = configs[process.env.TEST_TYPE] || configs.unit;
```_
## Berichterstattung
### Eingebaute Reporter
```bash
# Spec reporter (default)
npx mocha --reporter spec
# JSON reporter
npx mocha --reporter json
# TAP reporter
npx mocha --reporter tap
# Dot reporter
npx mocha --reporter dot
# Progress reporter
npx mocha --reporter progress
# Min reporter
npx mocha --reporter min
# Landing reporter
npx mocha --reporter landing
# List reporter
npx mocha --reporter list
```_
### Zollberichterstatter
```javascript
// reporters/custom-reporter.js
function CustomReporter(runner) {
const stats = runner.stats;
runner.on('start', function() {
console.log('🚀 Starting test run...');
});
runner.on('suite', function(suite) {
if (suite.root) return;
console.log(`📁 ${suite.title}`);
});
runner.on('test', function(test) {
console.log(` ▶️ ${test.title}`);
});
runner.on('pass', function(test) {
console.log(` ✅ ${test.title} (${test.duration}ms)`);
});
runner.on('fail', function(test, err) {
console.log(` ❌ ${test.title}`);
console.log(` ${err.message}`);
});
runner.on('end', function() {
console.log(`🏁 ${stats.passes} passed, ${stats.failures} failed`);
});
}
module.exports = CustomReporter;
// Usage
// npx mocha --reporter ./reporters/custom-reporter.js
```_
### Reporter mit Dateiausgang
```javascript
// reporters/file-reporter.js
const fs = require('fs');
const path = require('path');
function FileReporter(runner, options) {
const reportPath = options.reporterOptions?.output || 'test-results.json';
const results = {
stats: {},
tests: [],
failures: []
};
runner.on('start', function() {
results.stats.start = new Date();
});
runner.on('pass', function(test) {
results.tests.push({
title: test.title,
fullTitle: test.fullTitle(),
duration: test.duration,
state: 'passed'
});
});
runner.on('fail', function(test, err) {
results.tests.push({
title: test.title,
fullTitle: test.fullTitle(),
duration: test.duration,
state: 'failed',
error: err.message
});
results.failures.push({
title: test.fullTitle(),
error: err.message,
stack: err.stack
});
});
runner.on('end', function() {
results.stats.end = new Date();
results.stats.duration = results.stats.end - results.stats.start;
results.stats.passes = runner.stats.passes;
results.stats.failures = runner.stats.failures;
fs.writeFileSync(reportPath, JSON.stringify(results, null, 2));
console.log(`Report written to ${reportPath}`);
});
}
module.exports = FileReporter;
```_
### Mehrere Reporter
```bash
# Install multi-reporter
npm install --save-dev mocha-multi-reporters
# Configuration file
# multi-reporter-config.json
{
"reporterEnabled": "spec,json,tap",
"jsonReporterOptions": {
"output": "test-results.json"
},
"tapReporterOptions": {
"output": "test-results.tap"
}
}
# Run with multiple reporters
npx mocha --reporter mocha-multi-reporters --reporter-options configFile=multi-reporter-config.json
```_
## Browser-Testing
### Browser-Setup
```html
<!-- test/browser/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Mocha Browser Tests</title>
<link rel="stylesheet" href="https://unpkg.com/mocha/mocha.css">
</head>
<body>
<div id="mocha"></div>
<!-- Mocha -->
<script src="https://unpkg.com/mocha/mocha.js"></script>
<!-- Chai -->
<script src="https://unpkg.com/chai/chai.js"></script>
<!-- Setup -->
<script>
mocha.setup('bdd');
const expect = chai.expect;
</script>
<!-- Your code -->
<script src="../lib/calculator.js"></script>
<!-- Your tests -->
<script src="calculator.test.js"></script>
<!-- Run tests -->
<script>
mocha.run();
</script>
</body>
</html>
```_
### Browser Testdatei
```javascript
// test/browser/calculator.test.js
describe('Calculator (Browser)', function() {
it('should add numbers', function() {
expect(add(2, 3)).to.equal(5);
});
it('should work with DOM', function() {
const div = document.createElement('div');
div.textContent = 'Hello World';
document.body.appendChild(div);
expect(div.textContent).to.equal('Hello World');
document.body.removeChild(div);
});
it('should test localStorage', function() {
localStorage.setItem('test', 'value');
expect(localStorage.getItem('test')).to.equal('value');
localStorage.removeItem('test');
});
});
```_
### Webpack Integration
```javascript
// webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
entry: './test/browser/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'test-bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
}
};
```_
### Plüsch-Integration
```javascript
// test/browser/puppeteer.test.js
const puppeteer = require('puppeteer');
const { expect } = require('chai');
describe('Browser Tests with Puppeteer', function() {
let browser, page;
before(async function() {
browser = await puppeteer.launch();
page = await browser.newPage();
});
after(async function() {
await browser.close();
});
it('should run tests in browser', async function() {
await page.goto('file://' + __dirname + '/index.html');
// Wait for tests to complete
await page.waitForFunction(() => {
return window.mochaResults && window.mochaResults.complete;
});
const results = await page.evaluate(() => window.mochaResults);
expect(results.failures).to.equal(0);
});
});
```_
## Mocking
### Integration von Sinon
```javascript
const sinon = require('sinon');
const { expect } = require('chai');
describe('Mocking with Sinon', function() {
afterEach(function() {
sinon.restore();
});
it('should mock functions', function() {
const callback = sinon.fake();
const proxy = sinon.fake.returns(42);
callback('hello', 'world');
expect(callback.calledWith('hello', 'world')).to.be.true;
expect(proxy()).to.equal(42);
});
it('should stub methods', function() {
const user = {
getName: () => 'John',
setName: (name) => { this.name = name; }
};
const stub = sinon.stub(user, 'getName').returns('Jane');
expect(user.getName()).to.equal('Jane');
expect(stub.calledOnce).to.be.true;
});
it('should spy on methods', function() {
const user = {
getName: () => 'John'
};
const spy = sinon.spy(user, 'getName');
user.getName();
user.getName();
expect(spy.calledTwice).to.be.true;
});
});
```_
### HTTP Mocking
```javascript
const nock = require('nock');
const axios = require('axios');
const { expect } = require('chai');
describe('HTTP Mocking', function() {
afterEach(function() {
nock.cleanAll();
});
it('should mock HTTP requests', async function() {
nock('https://api.example.com')
.get('/users/1')
.reply(200, { id: 1, name: 'John' });
const response = await axios.get('https://api.example.com/users/1');
expect(response.data.name).to.equal('John');
});
it('should mock POST requests', async function() {
nock('https://api.example.com')
.post('/users', { name: 'Jane' })
.reply(201, { id: 2, name: 'Jane' });
const response = await axios.post('https://api.example.com/users', {
name: 'Jane'
});
expect(response.status).to.equal(201);
expect(response.data.id).to.equal(2);
});
it('should mock with delays', async function() {
nock('https://api.example.com')
.get('/slow')
.delay(1000)
.reply(200, { message: 'slow response' });
const start = Date.now();
await axios.get('https://api.example.com/slow');
const duration = Date.now() - start;
expect(duration).to.be.at.least(1000);
});
});
```_
### Modul Mocking
```javascript
const proxyquire = require('proxyquire');
const { expect } = require('chai');
describe('Module Mocking', function() {
it('should mock required modules', function() {
const dbMock = {
findUser: sinon.stub().returns({ id: 1, name: 'John' })
};
const UserService = proxyquire('../lib/user-service', {
'./database': dbMock
});
const user = UserService.getUser(1);
expect(user.name).to.equal('John');
expect(dbMock.findUser.calledWith(1)).to.be.true;
});
it('should mock with multiple dependencies', function() {
const mocks = {
'./database': {
connect: sinon.stub(),
query: sinon.stub().returns([])
},
'./logger': {
log: sinon.stub(),
error: sinon.stub()
}
};
const Service = proxyquire('../lib/service', mocks);
Service.initialize();
expect(mocks['./database'].connect.called).to.be.true;
expect(mocks['./logger'].log.called).to.be.true;
});
});
```_
## Erweiterte Funktionen
### Parallele Prüfung
```bash
# Install parallel testing
npm install --save-dev mocha-parallel-tests
# Run tests in parallel
npx mocha-parallel-tests
# Specify max parallel processes
npx mocha-parallel-tests --max-parallel 4
```_
### Prüffilter
```bash
# Run tests matching pattern
npx mocha --grep "should add"
# Run tests NOT matching pattern
npx mocha --grep "should add" --invert
# Run tests in specific files
npx mocha test/unit/**/*.test.js
# Run tests with tags
npx mocha --grep "@slow"
```_
```javascript
// Tag tests with comments
describe('Calculator', function() {
it('should add quickly @fast', function() {
// Fast test
});
it('should handle complex calculations @slow', function() {
// Slow test
});
});
```_
### Dynamische Testgeneration
```javascript
describe('Dynamic Tests', function() {
const testCases = [
{ input: [2, 3], expected: 5 },
{ input: [5, 7], expected: 12 },
{ input: [-1, 1], expected: 0 },
{ input: [0, 0], expected: 0 }
];
testCases.forEach(({ input, expected }) => {
it(`should add ${input[0]} + ${input[1]} = ${expected}`, function() {
expect(add(input[0], input[1])).to.equal(expected);
});
});
});
// Async dynamic tests
describe('API Endpoints', function() {
let endpoints;
before(async function() {
endpoints = await loadEndpointsConfig();
});
function createEndpointTest(endpoint) {
it(`should respond to ${endpoint.method} ${endpoint.path}`, async function() {
const response = await request(app)
[endpoint.method.toLowerCase()](endpoint.path);
expect(response.status).to.equal(endpoint.expectedStatus);
});
}
// Generate tests after loading config
before(function() {
endpoints.forEach(createEndpointTest);
});
});
```_
### Test Utilities
```javascript
// test/helpers/utils.js
const { expect } = require('chai');
function expectAsync(promise) {
return {
toResolve: async () => {
try {
const result = await promise;
return result;
} catch (error) {
throw new Error(`Expected promise to resolve, but it rejected with: ${error.message}`);
}
},
toReject: async (expectedError) => {
try {
await promise;
throw new Error('Expected promise to reject, but it resolved');
} catch (error) {
if (expectedError) {
expect(error.message).to.include(expectedError);
}
return error;
}
}
};
}
function createUser(overrides = {}) {
return {
id: Math.floor(Math.random() * 1000),
name: 'Test User',
email: 'test@example.com',
...overrides
};
}
module.exports = { expectAsync, createUser };
```_
### Testdatenfaktoren
```javascript
// test/factories/user-factory.js
class UserFactory {
static create(overrides = {}) {
return {
id: this.generateId(),
name: 'John Doe',
email: 'john@example.com',
age: 30,
active: true,
createdAt: new Date(),
...overrides
};
}
static createMany(count, overrides = {}) {
return Array.from({ length: count }, (_, index) =>
this.create({ id: index + 1, ...overrides })
);
}
static generateId() {
return Math.floor(Math.random() * 10000);
}
}
module.exports = UserFactory;
// Usage in tests
const UserFactory = require('./factories/user-factory');
describe('User Service', function() {
it('should process user', function() {
const user = UserFactory.create({ name: 'Jane' });
const result = processUser(user);
expect(result.displayName).to.equal('Jane');
});
it('should handle multiple users', function() {
const users = UserFactory.createMany(5);
expect(users).to.have.length(5);
});
});
```_
## Plugins
### Beliebte Plugins
```bash
# Chai plugins
npm install --save-dev chai-as-promised chai-http chai-string
# Sinon integration
npm install --save-dev sinon-chai
# Coverage
npm install --save-dev nyc
# Parallel testing
npm install --save-dev mocha-parallel-tests
# Multi-reporters
npm install --save-dev mocha-multi-reporters
```_
### Chai Plugins Nutzung
```javascript
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const chaiHttp = require('chai-http');
const chaiString = require('chai-string');
chai.use(chaiAsPromised);
chai.use(chaiHttp);
chai.use(chaiString);
const { expect } = chai;
describe('Chai Plugins', function() {
it('should test promises', async function() {
await expect(Promise.resolve('success')).to.eventually.equal('success');
await expect(Promise.reject(new Error('fail'))).to.be.rejected;
});
it('should test HTTP', function() {
return chai.request(app)
.get('/api/users')
.then(res => {
expect(res).to.have.status(200);
expect(res).to.be.json;
expect(res.body).to.be.an('array');
});
});
it('should test strings', function() {
expect('Hello World').to.startWith('Hello');
expect('Hello World').to.endWith('World');
expect('Hello World').to.equalIgnoreCase('hello world');
});
});
```_
### Benutzerdefinierte Plugin Entwicklung
```javascript
// chai-custom-plugin.js
module.exports = function(chai, utils) {
chai.Assertion.addMethod('validEmail', function() {
const obj = this._obj;
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
this.assert(
emailRegex.test(obj),
'expected #{this} to be a valid email',
'expected #{this} not to be a valid email'
);
});
chai.Assertion.addMethod('between', function(min, max) {
const obj = this._obj;
this.assert(
obj >= min && obj <= max,
'expected #{this} to be between #{exp1} and #{exp2}',
'expected #{this} not to be between #{exp1} and #{exp2}',
`${min} and ${max}`
);
});
};
// Usage
const chai = require('chai');
chai.use(require('./chai-custom-plugin'));
describe('Custom Plugin', function() {
it('should validate emails', function() {
expect('user@example.com').to.be.validEmail;
});
it('should check ranges', function() {
expect(5).to.be.between(1, 10);
});
});
```_
## CI/CD Integration
### GitHub Aktionen
```yaml
# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16, 18, 20]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Run coverage
run: npm run test:coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
file: ./coverage/lcov.info
```_
### Jenkins Pipeline
```groovy
// Jenkinsfile
pipeline {
agent any
tools {
nodejs '18'
}
stages {
stage('Install') {
steps {
sh 'npm ci'
}
}
stage('Test') {
steps {
sh 'npm test'
}
post {
always {
publishTestResults testResultsPattern: 'test-results.xml'
}
}
}
stage('Coverage') {
steps {
sh 'npm run test:coverage'
}
post {
always {
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'coverage',
reportFiles: 'index.html',
reportName: 'Coverage Report'
])
}
}
}
}
}
```_
### Docker Integration
```dockerfile
# Dockerfile.test
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
CMD ["npm", "test"]
```_
```yaml
# docker-compose.test.yml
version: '3.8'
services:
test:
build:
context: .
dockerfile: Dockerfile.test
environment:
- NODE_ENV=test
volumes:
- ./coverage:/app/coverage
```_
## Best Practices
### Prüforganisation
```javascript
// Good: Clear test structure
describe('UserService', function() {
describe('#createUser', function() {
context('with valid data', function() {
it('should create user successfully', function() {
// Test implementation
});
it('should return user with ID', function() {
// Test implementation
});
});
context('with invalid data', function() {
it('should throw validation error', function() {
// Test implementation
});
});
});
});
// Good: Descriptive test names
it('should return 404 when user does not exist', function() {
// Test implementation
});
it('should update user email when valid email provided', function() {
// Test implementation
});
```_
### Test Data Management
```javascript
// Good: Use factories for test data
const UserFactory = require('./factories/user-factory');
describe('User Tests', function() {
it('should validate user', function() {
const user = UserFactory.create({ email: 'invalid-email' });
expect(() => validateUser(user)).to.throw();
});
});
// Good: Clean up after tests
describe('Database Tests', function() {
afterEach(async function() {
await cleanupDatabase();
});
});
```_
### Async Best Practices
```javascript
// Good: Proper async handling
describe('Async Tests', function() {
it('should handle promises', async function() {
const result = await asyncOperation();
expect(result).to.exist;
});
it('should handle errors', async function() {
await expect(failingOperation()).to.be.rejected;
});
});
// Good: Proper timeout handling
describe('Slow Tests', function() {
this.timeout(10000);
it('should complete slow operation', async function() {
const result = await slowOperation();
expect(result).to.exist;
});
});
```_
## Debugging
### Debug Mode
```bash
# Run with debug output
DEBUG=mocha* npm test
# Run specific test with debug
npx mocha --grep "specific test" --inspect-brk
# Debug with VS Code
# Add to launch.json
{
"type": "node",
"request": "launch",
"name": "Mocha Debug",
"program": "${workspaceFolder}/node_modules/.bin/mocha",
"args": ["--timeout", "999999", "--colors", "${workspaceFolder}/test"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
```_
### Test Debugging
```javascript
describe('Debug Tests', function() {
it('should debug test', function() {
const data = { name: 'John', age: 30 };
console.log('Debug data:', data);
debugger; // Breakpoint for debugging
expect(data.name).to.equal('John');
});
it('should log test progress', function() {
console.log('Step 1: Setup');
const user = createUser();
console.log('Step 2: Process');
const result = processUser(user);
console.log('Step 3: Verify');
expect(result).to.exist;
});
});
```_
### Fehler Debugging
```javascript
describe('Error Debugging', function() {
it('should provide detailed error info', function() {
try {
const result = complexOperation();
expect(result.value).to.equal(42);
} catch (error) {
console.error('Test failed with error:', error);
console.error('Stack trace:', error.stack);
throw error;
}
});
});
```_
## Leistung
### Prüfleistung
```javascript
// Optimize test setup
describe('Performance Tests', function() {
// Use before/after for expensive setup
before(async function() {
this.database = await setupDatabase();
});
after(async function() {
await this.database.close();
});
// Avoid unnecessary work in tests
it('should be fast', function() {
// Minimal test implementation
expect(true).to.be.true;
});
});
Parallele Ausführung¶
```bash
Run tests in parallel¶
npx mocha-parallel-tests
Specify parallel workers¶
npx mocha-parallel-tests --max-parallel 4 ```_
Speicheroptimierung¶
```javascript // .mocharc.js module.exports = { // Optimize for memory usage timeout: 5000, bail: true, // Stop on first failure exit: true, // Force exit after tests
// Cleanup after tests require: ['test/helpers/cleanup.js'] }; ```_
Fehlerbehebung¶
Gemeinsame Themen¶
```javascript // Issue: Tests not running // Solution: Check file patterns and paths // .mocharc.json { "spec": "test/**/*.test.js", // Correct pattern "recursive": true }
// Issue: Async tests timing out // Solution: Increase timeout or fix async handling describe('Async Tests', function() { this.timeout(10000); // Increase timeout
it('should handle async', async function() { const result = await asyncOperation(); expect(result).to.exist; }); });
// Issue: Memory leaks // Solution: Proper cleanup afterEach(function() { // Clean up resources sinon.restore(); nock.cleanAll(); }); ```_
Debug Konfiguration¶
```javascript // Debug configuration issues console.log('Mocha config:', JSON.stringify(require('./.mocharc.json'), null, 2));
// Debug test discovery npx mocha --dry-run
// Debug reporter issues npx mocha --reporter json > test-results.json ```_
--
Zusammenfassung¶
Mocha ist ein flexibles und leistungsstarkes JavaScript-Test-Framework, das bietet:
- **Flexible Structure*: Unterstützung für BDD, TDD und benutzerdefinierte Schnittstellen
- **Async Support*: Native Unterstützung für Versprechungen, Callbacks und Async/await
- **Rich Ecosystem*: Umfangreiches Plugin-Ökosystem und Integrationen
- **Multiple Environments*: Runs in Node.js und Browser
- ** Umfassende Reporting**: Mehrere eingebaute Reporter und benutzerdefinierte Reporter-Unterstützung
- ** Fortgeschrittene Funktionen*: Parallele Tests, Testfilter und dynamische Testgeneration
- **CI/CD Ready*: Ausgezeichnete Integration mit kontinuierlichen Integrationssystemen
- **Debugging Support*: Reiche Debugging-Funktionen und Fehlerberichterstattung
Mocha zeichnet sich durch eine solide Grundlage für JavaScript-Tests mit seiner Flexibilität, umfangreichem Feature-Set und reifem Ökosystem aus. Sein unopinionierter Ansatz ermöglicht es Teams, Test-Workflows zu erstellen, die ihren spezifischen Bedürfnissen entsprechen und gleichzeitig exzellente Entwicklererfahrung und zuverlässige Testausführung beibehalten.
<= <= <= <================================================================================= Funktion copyToClipboard() {\cHFFFF} const commands = document.querySelectorAll('code'); alle Befehle = ''; Befehle. Für jede(cmd) => alle Befehle += cmd.textContent + '\n'); navigator.clipboard.writeText (allCommands); Alarm ('Alle Befehle, die in die Zwischenablage kopiert werden!'); }
Funktion generierenPDF() { Fenster.print(); }