The assert module provides functions for verifying invariants and writing tests.
Basic Assertions#
1import assert from 'assert';
2
3// assert(value) - truthy check
4assert(true); // OK
5assert(1); // OK
6assert('hello'); // OK
7// assert(false); // AssertionError
8// assert(0); // AssertionError
9// assert(''); // AssertionError
10
11// With custom message
12assert(value, 'Value must be truthy');
13
14// assert.ok() - same as assert()
15assert.ok(true);
16assert.ok(1 > 0, 'One should be greater than zero');Equality Assertions#
1import assert from 'assert';
2
3// Strict equality (===)
4assert.strictEqual(1, 1); // OK
5assert.strictEqual('a', 'a'); // OK
6// assert.strictEqual(1, '1'); // AssertionError
7
8// Loose equality (==)
9assert.equal(1, '1'); // OK (coercion)
10assert.equal(null, undefined); // OK
11
12// Not equal
13assert.notStrictEqual(1, 2); // OK
14assert.notStrictEqual(1, '1'); // OK
15assert.notEqual(1, 2); // OK
16
17// Deep equality (objects/arrays)
18assert.deepStrictEqual(
19 { a: 1, b: { c: 2 } },
20 { a: 1, b: { c: 2 } }
21); // OK
22
23assert.deepStrictEqual([1, 2, 3], [1, 2, 3]); // OK
24
25// Deep not equal
26assert.notDeepStrictEqual(
27 { a: 1 },
28 { a: 2 }
29); // OKStrict vs Legacy Mode#
1import assert from 'assert/strict';
2// Or: import { strict as assert } from 'assert';
3
4// In strict mode:
5// - assert.equal becomes assert.strictEqual
6// - assert.deepEqual becomes assert.deepStrictEqual
7// - assert.notEqual becomes assert.notStrictEqual
8
9assert.equal(1, 1); // Uses strictEqual
10// assert.equal(1, '1'); // AssertionError in strict mode
11
12// Always prefer strict mode for new code
13import { strict as assert } from 'assert';
14
15assert.deepEqual({ a: 1 }, { a: 1 }); // Uses deepStrictEqualError Assertions#
1import { strict as assert } from 'assert';
2
3// assert.throws - sync function throws
4assert.throws(
5 () => { throw new Error('Wrong!'); },
6 Error
7);
8
9// With message matcher
10assert.throws(
11 () => { throw new Error('Invalid input'); },
12 /Invalid/
13);
14
15// With error object
16assert.throws(
17 () => { throw new Error('Not found'); },
18 {
19 name: 'Error',
20 message: 'Not found'
21 }
22);
23
24// With custom validation
25assert.throws(
26 () => { throw new TypeError('Type error'); },
27 (err) => {
28 assert.strictEqual(err.name, 'TypeError');
29 assert.strictEqual(err.message, 'Type error');
30 return true; // Must return true
31 }
32);
33
34// assert.doesNotThrow
35assert.doesNotThrow(
36 () => { return 42; },
37 'Should not throw'
38);Async Assertions#
1import { strict as assert } from 'assert';
2
3// assert.rejects - async function rejects
4await assert.rejects(
5 async () => { throw new Error('Async error'); },
6 Error
7);
8
9await assert.rejects(
10 async () => { throw new Error('Failed'); },
11 {
12 name: 'Error',
13 message: 'Failed'
14 }
15);
16
17// With promise
18await assert.rejects(
19 Promise.reject(new Error('Rejected')),
20 /Rejected/
21);
22
23// assert.doesNotReject
24await assert.doesNotReject(
25 async () => { return 'success'; }
26);
27
28await assert.doesNotReject(
29 Promise.resolve('value')
30);Pattern Matching#
1import { strict as assert } from 'assert';
2
3// assert.match - regex matching
4assert.match('hello world', /world/);
5assert.match('test@example.com', /^\S+@\S+\.\S+$/);
6
7// assert.doesNotMatch
8assert.doesNotMatch('hello', /world/);
9assert.doesNotMatch('abc', /\d+/);
10
11// Object pattern matching
12const user = {
13 id: 1,
14 name: 'John',
15 email: 'john@example.com',
16 createdAt: new Date()
17};
18
19// Partial match with properties
20assert.deepStrictEqual(
21 { id: user.id, name: user.name },
22 { id: 1, name: 'John' }
23);
24
25// Custom matcher
26function assertMatch(obj, pattern) {
27 for (const [key, matcher] of Object.entries(pattern)) {
28 if (typeof matcher === 'function') {
29 assert.ok(matcher(obj[key]), `${key} failed validation`);
30 } else if (matcher instanceof RegExp) {
31 assert.match(obj[key], matcher);
32 } else {
33 assert.strictEqual(obj[key], matcher);
34 }
35 }
36}
37
38assertMatch(user, {
39 id: 1,
40 name: 'John',
41 email: /^.+@.+\..+$/,
42 createdAt: d => d instanceof Date
43});Fail Assertions#
1import { strict as assert } from 'assert';
2
3// Always fails
4// assert.fail();
5// assert.fail('Custom error message');
6// assert.fail(new Error('Custom error'));
7
8// Conditional fail
9function processValue(value) {
10 if (typeof value !== 'number') {
11 assert.fail('Expected number');
12 }
13 return value * 2;
14}
15
16// Unreachable code
17function handleCase(type) {
18 switch (type) {
19 case 'a':
20 return 1;
21 case 'b':
22 return 2;
23 default:
24 assert.fail(`Unexpected type: ${type}`);
25 }
26}Testing Examples#
1import { strict as assert } from 'assert';
2import { describe, it } from 'node:test';
3
4describe('Calculator', () => {
5 describe('add', () => {
6 it('should add two numbers', () => {
7 assert.strictEqual(add(2, 3), 5);
8 });
9
10 it('should handle negative numbers', () => {
11 assert.strictEqual(add(-1, 1), 0);
12 });
13
14 it('should throw for non-numbers', () => {
15 assert.throws(
16 () => add('a', 'b'),
17 TypeError
18 );
19 });
20 });
21});
22
23// Async testing
24describe('UserService', () => {
25 it('should fetch user by id', async () => {
26 const user = await userService.getById(1);
27
28 assert.deepStrictEqual(user, {
29 id: 1,
30 name: 'John',
31 email: 'john@example.com'
32 });
33 });
34
35 it('should throw for non-existent user', async () => {
36 await assert.rejects(
37 userService.getById(999),
38 { name: 'NotFoundError' }
39 );
40 });
41});Custom Assertions#
1import { strict as assert } from 'assert';
2
3// Create custom assertion functions
4function assertIsPositive(value, message) {
5 assert.ok(
6 typeof value === 'number' && value > 0,
7 message || `Expected positive number, got ${value}`
8 );
9}
10
11function assertArrayIncludes(arr, item, message) {
12 assert.ok(
13 arr.includes(item),
14 message || `Array does not include ${item}`
15 );
16}
17
18function assertType(value, type, message) {
19 const actualType = typeof value;
20 assert.strictEqual(
21 actualType,
22 type,
23 message || `Expected ${type}, got ${actualType}`
24 );
25}
26
27function assertInstanceOf(value, constructor, message) {
28 assert.ok(
29 value instanceof constructor,
30 message || `Expected instance of ${constructor.name}`
31 );
32}
33
34// Usage
35assertIsPositive(5); // OK
36assertArrayIncludes([1, 2, 3], 2); // OK
37assertType('hello', 'string'); // OK
38assertInstanceOf(new Date(), Date); // OKAssertionError#
1import { strict as assert, AssertionError } from 'assert';
2
3// Access AssertionError properties
4try {
5 assert.strictEqual(1, 2);
6} catch (err) {
7 if (err instanceof AssertionError) {
8 console.log('Actual:', err.actual); // 1
9 console.log('Expected:', err.expected); // 2
10 console.log('Operator:', err.operator); // 'strictEqual'
11 console.log('Message:', err.message);
12 }
13}
14
15// Create custom AssertionError
16const error = new AssertionError({
17 message: 'Custom assertion failed',
18 actual: 'foo',
19 expected: 'bar',
20 operator: 'customAssert'
21});
22
23// Useful for custom assertions
24function assertBetween(value, min, max) {
25 if (value < min || value > max) {
26 throw new AssertionError({
27 message: `Expected ${value} to be between ${min} and ${max}`,
28 actual: value,
29 expected: `${min} <= value <= ${max}`,
30 operator: 'assertBetween'
31 });
32 }
33}Validation Helpers#
1import { strict as assert } from 'assert';
2
3// Validation using assertions
4function validateUser(user) {
5 assert.ok(user, 'User is required');
6 assert.ok(typeof user.name === 'string', 'Name must be a string');
7 assert.ok(user.name.length >= 2, 'Name must be at least 2 characters');
8 assert.match(user.email, /^\S+@\S+\.\S+$/, 'Invalid email format');
9 assert.ok(Number.isInteger(user.age), 'Age must be an integer');
10 assert.ok(user.age >= 0 && user.age <= 150, 'Age must be between 0 and 150');
11
12 return true;
13}
14
15// Schema validation
16function assertSchema(obj, schema) {
17 for (const [key, validator] of Object.entries(schema)) {
18 const value = obj[key];
19
20 if (typeof validator === 'string') {
21 assert.strictEqual(typeof value, validator, `${key} must be ${validator}`);
22 } else if (typeof validator === 'function') {
23 assert.ok(validator(value), `${key} failed validation`);
24 } else if (validator instanceof RegExp) {
25 assert.match(String(value), validator, `${key} format invalid`);
26 }
27 }
28}
29
30assertSchema(user, {
31 name: 'string',
32 age: 'number',
33 email: /^\S+@\S+\.\S+$/,
34 active: v => typeof v === 'boolean'
35});Best Practices#
Choosing Assertions:
✓ Use strict mode always
✓ Use deepStrictEqual for objects
✓ Use strictEqual for primitives
✓ Use throws/rejects for errors
Messages:
✓ Provide descriptive messages
✓ Include actual values in messages
✓ Make failures easy to debug
✓ Use template literals
Testing:
✓ One assertion per behavior
✓ Test edge cases
✓ Test error conditions
✓ Use async assertions for promises
Avoid:
✗ Legacy loose equality
✗ Catching AssertionErrors silently
✗ Too many assertions per test
✗ Using assert for user input validation
Conclusion#
The Node.js assert module provides essential functions for testing and invariant checking. Always use strict mode (assert/strict) for reliable equality checks. Use throws and rejects for error testing, match for regex validation, and deepStrictEqual for object comparison. While assert is great for tests and development checks, consider proper validation libraries for user input validation in production code.