Loading lesson path
Why Test Your Node.js Applications? Testing is an essential part of software development that provides numerous benefits:
Find and fix errors before they reach production
Tests serve as executable documentation for your code
Build confidence in making changes and refactoring code
Help team members understand how code should work
Types of Testing in Node.js
Unit tests verify that individual components (functions, methods, classes) work as expected in isolation, typically using mocks for dependencies.
Example: Unit Testing with Node.js Assert calculator.js function add(a, b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw new Error('Both arguments must be numbers');
}
return a + b;
}
function subtract(a, b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw new Error('Both arguments must be numbers');
}
return a - b;
}
module.exports = { add, subtract };
test/calculator.test.js const assert = require('assert');
const { add, subtract } = require('./calculator');
// Test the add function assert.strictEqual(add(1, 2), 3, 'Addition not working correctly');
assert.strictEqual(add(-1, 1), 0, 'Addition with negative numbers not working');
// Test the subtract function assert.strictEqual(subtract(5, 2), 3, 'Subtraction not working correctly');
assert.strictEqual(subtract(2, 5), -3, 'Subtraction resulting in negative not working');
console.log('All tests passed!');Integration tests verify that multiple components work together correctly, such as testing database operations, API endpoints, or third-party service interactions.
Example: Testing a Simple API Endpoint app.js const express = require('express');
const app = express();
app.get('/users', (req, res) => {
res.json([
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
]);
});
module.exports = app;
test.js const assert = require('assert');
const http = require('http');
const app = require('./app');
// Start the server const server = app.listen(8080);// Make a request to the API
http.get('http://localhost:8080/users', (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
const users = JSON.parse(data);
// Verify the response assert.strictEqual(res.statusCode, 200, 'Status code should be 200');
assert.strictEqual(users.length, 2, 'Should return two users');
assert.strictEqual(users[0].name, 'Alice', 'First user should be Alice');
assert.strictEqual(users[1].name, 'Bob', 'Second user should be Bob');
console.log('API test passed!');
// Close the server server.close();
});
}).on('error', (err) => {
console.error('Test failed:', err);
server.close();
});End-to-end tests verify the entire application flow from start to finish, simulating real user scenarios and interactions.
Playwright, Cypress, or
to automate browser interactions.
End-to-end tests are more complex to set up and maintain but provide the most thorough validation of your application's functionality.
Formula
Test - Driven Development (TDD)
Test - Driven Development is a software development approach where you:Write a test that defines a function or improvement Run the test, which should fail because the function doesn't exist yet Write the simplest code to make the test pass
Formula
TDD Example: Developing a Password Validator password - validator.test.js// 1. Write the test first const assert = require('assert');
const validatePassword = require('./password-validator');
// Test for password length assert.strictEqual(validatePassword('abc12'), false, 'Should reject passwords shorter than 8 characters');
assert.strictEqual(validatePassword('abcdef123'), true, 'Should accept passwords 8+ characters long');
// Test for number requirement assert.strictEqual(validatePassword('abcdefgh'), false, 'Should reject passwords without numbers');
assert.strictEqual(validatePassword('abcdefg1'), true, 'Should accept passwords with numbers');
console.log('All password validation tests passed!');Formula
// 2. Run the test - it will fail because validatePassword doesn't exist yet password - validator.js// 3. Write the simplest code to pass the tests function validatePassword(password) {
// Check length (at least 8 characters)
if (password.length < 8) {
return false;
}
// Check if it contains at least one number if (!/\d/.test(password)) {
return false;
}
return true;
}
module.exports = validatePassword;Formula
// 4. Run the tests again - they should pass now// 5. Refactor if needed, then repeat for new requirements
Functions that produce the same output for the same input without side effects are easier to test
Pass dependencies to functions rather than creating them inside