Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. Node.js Dictionary
  3. test

test

'Jest' is a widely-used JavaScript testing framework. It is commonly used for unit and integration testing in Node.js projects, and comes with built-in mocking capabilities and code coverage measurement. Since Node.js v18, a built-in test runner (node:test) is also available.

Basic Jest Syntax

// describe: group tests together
describe('group name', function() {

    // it or test: individual test case
    it('test description', function() {

        // expect: assertion (verify expected value)
        expect(actualValue).toBe(expectedValue);
    });

});

Key Matchers (expect)

MatcherOverview
toBe(value)Compares using strict equality (===). Used for primitive values.
toEqual(value)Recursively compares the contents of objects or arrays. Checks by value, not reference.
toBeTruthy()Verifies that the value is truthy.
toBeFalsy()Verifies that the value is falsy.
toBeNull()Verifies that the value is null.
toBeUndefined()Verifies that the value is undefined.
toContain(item)Verifies that an array contains the specified element, or that a string contains a substring.
toThrow(error)Verifies that a function throws an exception.
toHaveBeenCalled()Verifies that a mock function was called at least once.
toHaveBeenCalledWith(...args)Verifies that a mock function was called with the specified arguments.
not.toBe(value)Prefixing with not creates a negated assertion.

describe / it — Writing Basic Tests

'describe()' groups tests together, and 'it()' or 'test()' defines individual test cases. Extracting the logic under test into a function makes the code easier to test.

calc.js
// Export the functions under test
function add(a, b) {
    return a + b;
}

function multiply(a, b) {
    return a * b;
}

function divide(a, b) {
    if (b === 0) {
        throw new Error('Cannot divide by zero');
    }
    return a / b;
}

module.exports = { add: add, multiply: multiply, divide: divide };
calc.test.js
var calc = require('./calc');

// Use describe to create test groups
describe('calc module tests', function() {

    // Tests for addition
    describe('add()', function() {

        it('can add two positive integers', function() {
            // Attack power calculation for Yagami Iori
            expect(calc.add(100, 50)).toBe(150);
        });

        it('can add negative numbers', function() {
            expect(calc.add(-30, 80)).toBe(50);
        });

    });

    // Tests for multiplication
    describe('multiply()', function() {

        it('can multiply two numbers', function() {
            // Combo damage calculation for Kusanagi Kyo
            expect(calc.multiply(25, 4)).toBe(100);
        });

    });

    // Tests for division (including error cases)
    describe('divide()', function() {

        it('can divide normally', function() {
            expect(calc.divide(100, 4)).toBe(25);
        });

        it('throws an error when dividing by zero', function() {
            // Terry Bogard special move input error
            expect(function() {
                calc.divide(100, 0);
            }).toThrow('Cannot divide by zero');
        });

    });

});
npx jest calc.test.js
 PASS  ./calc.test.js
  calc module tests
    add()
      ✓ can add two positive integers (2 ms)
      ✓ can add negative numbers
    multiply()
      ✓ can multiply two numbers
    divide()
      ✓ can divide normally
      ✓ throws an error when dividing by zero

Test Suites: 1 passed, 1 total
Tests:       5 passed, 5 total

beforeEach / afterEach — Setup and Cleanup

To run shared logic before and after each test case, use 'beforeEach()' and 'afterEach()'. This is important for keeping tests independent from one another.

fighter.test.js
// Simple class under test (ES5 style)
function Fighter(name, hp) {
    this.name = name;
    this.hp = hp;
    this.log = [];
}

Fighter.prototype.takeDamage = function(amount) {
    this.hp = Math.max(0, this.hp - amount);
    this.log.push('damage: ' + amount);
};

Fighter.prototype.isDefeated = function() {
    return this.hp === 0;
};

// Test suite
describe('Fighter class tests', function() {

    var iori;

    // Create a fresh instance before each test
    beforeEach(function() {
        iori = new Fighter('Yagami Iori', 1000);
    });

    // Cleanup after each test if needed
    afterEach(function() {
        // Add cleanup logic here if necessary
    });

    it('HP decreases when taking damage', function() {
        iori.takeDamage(300);
        expect(iori.hp).toBe(700);
    });

    it('HP does not go below zero even with excessive damage', function() {
        iori.takeDamage(1500);
        expect(iori.hp).toBe(0);
    });

    it('HP reaching zero triggers defeat', function() {
        iori.takeDamage(1000);
        expect(iori.isDefeated()).toBe(true);
    });

    it('damage log is recorded', function() {
        iori.takeDamage(200);
        iori.takeDamage(300);
        // Verify array contents
        expect(iori.log).toEqual(['damage: 200', 'damage: 300']);
    });

});
npx jest fighter.test.js
 PASS  ./fighter.test.js
  Fighter class tests
    ✓ HP decreases when taking damage (1 ms)
    ✓ HP does not go below zero even with excessive damage
    ✓ HP reaching zero triggers defeat
    ✓ damage log is recorded

Tests: 4 passed, 4 total

jest.fn() — Mock Functions

To isolate external dependencies (databases, APIs, etc.) from tests, create mock functions with 'jest.fn()'. Call history and return values can be verified and controlled.

mock_function.test.js
// Testing callbacks with mock functions
describe('jest.fn() mocking', function() {

    it('verifies that a callback is called correctly', function() {
        // Create a mock function (a dummy function that records calls)
        var onWin = jest.fn();

        // Function under test: calls callback on victory
        function battle(fighter, opponent, callback) {
            if (fighter.power > opponent.power) {
                callback(fighter.name + ' wins!');
            }
        }

        var kyo  = { name: 'Kusanagi Kyo',  power: 1000 };
        var iori = { name: 'Yagami Iori',   power: 800 };

        battle(kyo, iori, onWin);

        // Verify the callback was called once
        expect(onWin).toHaveBeenCalledTimes(1);

        // Verify it was called with the correct argument
        expect(onWin).toHaveBeenCalledWith('Kusanagi Kyo wins!');
    });

    it('mockReturnValue can control mock return values', function() {
        var getRank = jest.fn();

        // Set values to return on each call
        getRank.mockReturnValueOnce('S').mockReturnValueOnce('A').mockReturnValue('B');

        expect(getRank()).toBe('S');   // 1st call
        expect(getRank()).toBe('A');   // 2nd call
        expect(getRank()).toBe('B');   // 3rd call and beyond
        expect(getRank()).toBe('B');
    });

    it('call history can be verified', function() {
        var logAttack = jest.fn();

        logAttack('Mai Shiranui', 'Shiranui Dan');
        logAttack('Ryo Sakazaki', 'Haoh Shoukouken');

        // Verify it was called twice in total
        expect(logAttack).toHaveBeenCalledTimes(2);

        // Verify the arguments of each call
        expect(logAttack.mock.calls[0]).toEqual(['Mai Shiranui', 'Shiranui Dan']);
        expect(logAttack.mock.calls[1]).toEqual(['Ryo Sakazaki', 'Haoh Shoukouken']);
    });

});
npx jest mock_function.test.js
 PASS  ./mock_function.test.js
  jest.fn() mocking
    ✓ verifies that a callback is called correctly (2 ms)
    ✓ mockReturnValue can control mock return values
    ✓ call history can be verified

Tests: 3 passed, 3 total

node:test — Node.js Built-in Test Runner

Since Node.js v18, a test runner is built into the standard library and can be used without installing external packages like Jest. Import the node:test module to use it. The syntax is similar to Jest, but it runs with the node command alone.

builtin_test.mjs
// Use the Node.js built-in test runner (v18+)
import test from 'node:test';
import assert from 'node:assert/strict';

// Basic test
test('addition works correctly', function(t) {
    assert.equal(1 + 1, 2);
    assert.equal(100 + 200, 300);
});

// Subtests (equivalent to describe)
test('Yagami Iori skill tests', async function(t) {

    await t.test('flame claw damage calculation', function() {
        var damage = 120 * 3; // 3-hit combo
        assert.equal(damage, 360);
    });

    await t.test('50% damage reduction on guard', function() {
        var damage = 360;
        assert.equal(Math.floor(damage * 0.5), 180);
    });

});

// Verify that an exception is thrown
test('division by zero throws an exception', function() {
    function divide(a, b) {
        if (b === 0) throw new Error('Division by zero error');
        return a / b;
    }

    assert.throws(function() {
        divide(10, 0);
    }, { message: 'Division by zero error' });
});
node --test builtin_test.mjs
▶ addition works correctly
  ✓ addition works correctly (0.5ms)
▶ Yagami Iori skill tests
  ▶ flame claw damage calculation
    ✓ flame claw damage calculation (0.2ms)
  ▶ 50% damage reduction on guard
    ✓ 50% damage reduction on guard (0.1ms)
  ✓ Yagami Iori skill tests (1.2ms)
▶ division by zero throws an exception
  ✓ division by zero throws an exception (0.3ms)
ℹ tests 4
ℹ pass 4
ℹ fail 0

Installing and Configuring Jest

Jest is installed via npm or Yarn. After installing, register the test command in the scripts field of package.json so it can be run with npm test.

npm install --save-dev jest
package.json
{
    "scripts": {
        "test": "jest"
    },
    "devDependencies": {
        "jest": "^29.0.0"
    }
}

Name test files *.test.js or *.spec.js and Jest will detect them automatically. Placing test files in a __tests__ directory is also a common approach.

Common Mistakes

Forgetting await in async tests

When writing assertions for async functions without await, the test completes before the Promise resolves, causing the test to always pass. Cases that should fail will go undetected, resulting in incorrect test results.

async function fetchHp(name) {
    return new Promise(function(resolve) {
        setTimeout(function() { resolve(1000); }, 100);
    });
}

// NG: without await, the test completes immediately and always passes
it('can fetch HP (NG)', function() {
    var promise = fetchHp('Yagami Iori');
    expect(promise).resolves.toBe(1000);
});

// OK: use async/await to wait until the Promise resolves
it('can fetch HP (OK)', async function() {
    var hp = await fetchHp('Yagami Iori');
    expect(hp).toBe(1000);
});

Alternatively, returning the Promise to Jest with return achieves the same waiting behavior.

Forgetting to restore mocks

After setting up a mock with jest.spyOn(), failing to call jest.restoreAllMocks() in afterEach or afterAll causes the mock to affect other test cases, producing unexpected results.

describe('mock cleanup', function() {

    // Restore mocks after each test with afterEach
    afterEach(function() {
        jest.restoreAllMocks();
    });

    it('mocking console.log', function() {
        var spy = jest.spyOn(console, 'log').mockImplementation(function() {});
        console.log('Kusanagi Kyo output');
        expect(spy).toHaveBeenCalledWith('Kusanagi Kyo output');
    });

    it('console.log is restored in this test', function() {
        console.log('Yagami Iori normal output');
    });

});

Setting restoreMocks: true in jest.config.js automatically restores mocks after each test.

Overview

Jest is one of the most widely-used testing frameworks in Node.js projects. Its simple structure of grouping tests with describe() and defining individual cases with it()/test() makes it easy to write readable test code.

Using the mock feature (jest.fn()) isolates external dependencies and improves test speed and reproducibility. Using beforeEach()/afterEach() to share setup and teardown logic across tests prevents state pollution between test cases.

The node:test module built into Node.js v18+ runs without any external packages, making it useful for projects that want to minimize dependencies or for testing small scripts. For full-featured projects, Jest's coverage measurement, snapshot testing, and other rich features are useful.

If you find any errors or copyright issues, please .