🧪 Testing in Node.js: Your Code’s Safety Net
Imagine you’re building a treehouse. Before letting your friends climb up, wouldn’t you shake each branch to make sure it’s strong? That’s exactly what testing does for your code!
🎯 What is Testing?
Testing is like being a detective for your own code. You check if everything works the way it should—before your users find the bugs!
Think of it this way:
- You write code = You bake a cake
- You test code = You taste the cake before serving it
- Tests pass = The cake is delicious! 🎂
- Tests fail = Oops, you forgot the sugar!
Node.js gives you two built-in tools for testing:
assertmodule — The simple checkertestmodule — The complete testing lab
📦 The Assert Module: Your Simple Truth Checker
What is Assert?
The assert module is like a true/false machine. You give it a statement, and it tells you: “Yes, that’s true!” or “STOP! That’s wrong!”
const assert = require('assert');
// Is 2 + 2 equal to 4?
assert.strictEqual(2 + 2, 4);
// ✅ Passes silently
// Is 2 + 2 equal to 5?
assert.strictEqual(2 + 2, 5);
// ❌ BOOM! AssertionError!
Why “Assert”?
“Assert” means “to say something is true”. When you assert something in code, you’re saying: “I promise this is true. If it’s not, stop everything!”
⚖️ Equality Assertions: Checking if Things Match
Node.js gives you different ways to check if things are equal. It’s like asking:
- “Do they look the same?” vs “Are they exactly the same?”
The Three Levels of Equality
graph TD A["🔍 Equality Checks"] --> B["equal<br>Loose check"] A --> C["strictEqual<br>Strict check"] A --> D["deepStrictEqual<br>Deep check"] B --> E["&#39;5&#39; == 5 ✅"] C --> F["&#39;5&#39; === 5 ❌"] D --> G["Objects & Arrays"]
1. assert.equal() — The Relaxed Friend
const assert = require('assert');
// Loose equality (uses ==)
assert.equal('5', 5);
// ✅ Passes! (string '5' == number 5)
⚠️ Warning: This is like a friend who says “close enough!” Not recommended!
2. assert.strictEqual() — The Precise Friend
const assert = require('assert');
// Strict equality (uses ===)
assert.strictEqual(5, 5);
// ✅ Passes!
assert.strictEqual('5', 5);
// ❌ Fails! String is not a number
This is your best friend for simple values!
3. assert.deepStrictEqual() — The Detective
When comparing objects or arrays, you need the deep checker:
const assert = require('assert');
const basket1 = { apples: 3, oranges: 2 };
const basket2 = { apples: 3, oranges: 2 };
// Are they the same object? NO!
assert.strictEqual(basket1, basket2);
// ❌ Fails! Different objects
// Do they have the same stuff? YES!
assert.deepStrictEqual(basket1, basket2);
// ✅ Passes!
Quick Reference: Which to Use?
| Type | Use For | Example |
|---|---|---|
strictEqual |
Numbers, strings, booleans | 5 === 5 |
deepStrictEqual |
Objects, arrays | {a:1} vs {a:1} |
notStrictEqual |
Check things are different | 5 !== 3 |
🧪 The Test Module: Your Complete Testing Lab
What is the Test Module?
While assert just checks one thing, the test module lets you organize many checks into a beautiful testing laboratory!
Think of it like this:
- Assert = One question on a quiz
- Test module = The entire quiz, organized into sections
const { test } = require('node:test');
const assert = require('assert');
test('Math still works', () => {
assert.strictEqual(2 + 2, 4);
});
Running Your Tests
node --test mytest.js
You’ll see beautiful output:
✔ Math still works (0.5ms)
🏗️ Test Structure: Building Your Test House
The Basic Building Block
Every test has a name and a function:
const { test } = require('node:test');
test('name of your test', () => {
// Your checking code goes here
});
Nesting Tests: Rooms Inside Rooms
You can organize tests into groups using describe:
const { test, describe } = require('node:test');
const assert = require('assert');
describe('Calculator', () => {
test('adds numbers', () => {
assert.strictEqual(2 + 3, 5);
});
test('subtracts numbers', () => {
assert.strictEqual(10 - 4, 6);
});
});
graph TD A["🏠 describe: Calculator"] --> B["🧪 test: adds numbers"] A --> C["🧪 test: subtracts"] A --> D["🧪 test: multiplies"]
Async Tests: Waiting Patiently
Some code takes time (like fetching data). Use async/await:
const { test } = require('node:test');
const assert = require('assert');
test('async operation', async () => {
const result = await fetchData();
assert.strictEqual(result, 'expected');
});
🪝 Test Hooks: Setting the Stage
What are Hooks?
Imagine you’re doing science experiments. Before each experiment, you need to:
- Clean your equipment (beforeEach)
- Set up materials (beforeEach)
After each experiment:
- Record results (afterEach)
- Clean up (afterEach)
Hooks do exactly this for your tests!
The Four Hooks
graph TD A["🎬 before<br>Once at start"] --> B["🔁 beforeEach<br>Before each test"] B --> C["🧪 Test 1"] C --> D["🔁 afterEach<br>After each test"] D --> E["🔁 beforeEach"] E --> F["🧪 Test 2"] F --> G["🔁 afterEach"] G --> H["🎬 after<br>Once at end"]
Hooks in Action
const {
test,
describe,
before,
after,
beforeEach,
afterEach
} = require('node:test');
describe('Database Tests', () => {
before(() => {
console.log('🚀 Starting database');
});
after(() => {
console.log('🛑 Closing database');
});
beforeEach(() => {
console.log('📝 Clearing test data');
});
afterEach(() => {
console.log('🧹 Cleaning up');
});
test('can save user', () => {
// Test code here
});
test('can find user', () => {
// Test code here
});
});
Output:
🚀 Starting database
📝 Clearing test data
✔ can save user
🧹 Cleaning up
📝 Clearing test data
✔ can find user
🧹 Cleaning up
🛑 Closing database
✅ Test Assertions: Your Checking Toolkit
Beyond Equal: More Ways to Check
The test module gives you powerful assertions:
1. Check if Something Throws an Error
const { test } = require('node:test');
const assert = require('assert');
test('throws on invalid input', () => {
assert.throws(() => {
throw new Error('Oops!');
});
});
2. Check if Something Rejects (Async)
test('rejects bad promise', async () => {
await assert.rejects(
async () => {
throw new Error('Failed!');
},
{ message: 'Failed!' }
);
});
3. Check if Something Does NOT Throw
test('should not throw', () => {
assert.doesNotThrow(() => {
return 'I am safe!';
});
});
4. Check Truthiness
test('values are truthy', () => {
assert.ok(true); // ✅
assert.ok(1); // ✅
assert.ok('hello'); // ✅
assert.ok([]); // ✅ (empty array is truthy!)
});
Complete Assertion Cheatsheet
| Assertion | What it Checks |
|---|---|
strictEqual(a, b) |
a === b |
notStrictEqual(a, b) |
a !== b |
deepStrictEqual(a, b) |
Objects match exactly |
ok(value) |
Value is truthy |
throws(fn) |
Function throws error |
rejects(fn) |
Async function rejects |
fail(msg) |
Always fails (marks TODO) |
🎮 Putting It All Together
Here’s a complete, real-world test file:
const {
test,
describe,
beforeEach
} = require('node:test');
const assert = require('assert');
// Our code to test
function Calculator() {
this.value = 0;
}
Calculator.prototype.add = function(n) {
this.value += n;
};
// Our tests
describe('Calculator', () => {
let calc;
beforeEach(() => {
calc = new Calculator();
});
test('starts at zero', () => {
assert.strictEqual(calc.value, 0);
});
test('can add numbers', () => {
calc.add(5);
calc.add(3);
assert.strictEqual(calc.value, 8);
});
});
🌟 Key Takeaways
graph TD A["🧪 Node.js Testing"] --> B["assert module<br>Simple checks"] A --> C["test module<br>Organized tests"] B --> D["strictEqual<br>deepStrictEqual<br>throws, ok"] C --> E["test & describe<br>Structure"] C --> F["Hooks<br>Setup & cleanup"] C --> G["Assertions<br>Many check types"]
Remember:
- 🎯 Assert = Your truth checker
- 🏗️ Test = Your organized lab
- 🪝 Hooks = Setup and cleanup helpers
- ✅ Assertions = Many ways to verify
Now go forth and test your code! Your future self (and your users) will thank you. 🚀
