JavaScript
Node.js
test
Jest

Node.js + Jest でモジュールの関数とクラスをモック化してユニットテストするサンプルコード


概要


  • JavaScript テストフレームワークの Jest を使う

  • jest.mock を使用してモックを作成し、ユニットテストを実行する


npm init で開発用ディレクトリを用意

$ npm init

This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (hello)
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to /Users/me/hello/package.json:

{
"name": "hello",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}

Is this OK? (yes)


Jest をインストール

$ npm install --save-dev jest


> fsevents@1.2.8 install /Users/me/hello/node_modules/fsevents
> node install

node-pre-gyp WARN Using request for node-pre-gyp https download
[fsevents] Success: "/Users/me/hello/node_modules/fsevents/lib/binding/Release/node-v64-darwin-x64/fse.node" is installed via remote
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN hello@1.0.0 No description
npm WARN hello@1.0.0 No repository field.

+ jest@24.7.1
added 558 packages from 373 contributors and audited 850808 packages in 12.066s
found 0 vulnerabilities

$ cat package.json
{
"name": "hello",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"jest": "^24.7.1"
}
}

package.json の scipts.test の箇所を Jest を実行するように書き換える。

$ cat package.json 

{
"name": "hello",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "jest"
},
"author": "",
"license": "ISC",
"devDependencies": {
"jest": "^24.7.1"
}
}


サンプルコードを用意


getrandomint.js


getrandomint.js

// ランダムな値を返す関数

function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}

module.exports = getRandomInt;



getrandomint.test.js


getrandomint.test.js

const getRandomInt = require('./getrandomint');

// ランダムな値ではテストできないので
// 指定した値を返すモック関数を作成
jest.mock('./getrandomint');
const getRandomIntMock = jest.fn((max) => {return max;});
getRandomInt.mockImplementation(getRandomIntMock);

// 指定した値が返ってくるかテスト
test('getRandomInt', () => {
expect(getRandomInt(100)).toBe(100);
});



random.js


random.js

// ランダムな値を返すクラス

class Random {
constructor() {
}
getInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
}

module.exports = Random;



random.test.js


random.test.js

const Random = require('./random');

// ランダムな値ではテストできないので
// 指定した値を返すモッククラスを作成
jest.mock('./random');
const getIntMock = jest.fn((max) => {return max});
Random.mockImplementation(() => {
return {
getInt: getIntMock,
};
});

// 指定した値が返ってくるかテスト
test('Random.getInt', () => {
expect(new Random().getInt(100)).toBe(100);
});



foobar.js


foobar.js

// getRandomInt 関数と Random クラスを使用する

const getRandomInt = require('./getrandomint');
const Random = require('./random');

// ランダムな数を2つ足して返す関数
function foobar(num) {
const a = getRandomInt(num);
const b = new Random().getInt(num);
return a + b;
}

module.exports = foobar;



foobar.test.js


foobar.test.js

// getRandomInt 関数を読み込む

const getRandomInt = require('./getrandomint');

// ランダムな値ではテストできないので
// 指定した値を返すモック関数を作成
jest.mock('./getrandomint');
const getRandomIntMock = jest.fn((max) => {return max;});
getRandomInt.mockImplementation(getRandomIntMock);

// Random クラスを読み込む
const Random = require('./random');

// ランダムな値ではテストできないので
// 指定した値を返すモッククラスを作成
jest.mock('./random');
const getIntMock = jest.fn((max) => {return max});
Random.mockImplementation(() => {
return {
getInt: getIntMock,
};
});

// getRandomInt 関数とRandom クラスを使用している foobar 関数を読み込む
const foobar = require('./foobar');

// 100を指定したら200が返ってくるかテスト
// foobar 関数の内部では getRandomInt 関数と Random クラスのモックが呼び出されている
test('foobar(100)', () => {
expect(foobar(100)).toBe(200);
});



テストを実行する

$ npm test


> hello@1.0.0 test /Users/me/hello
> jest

PASS ./random.test.js
PASS ./getrandomint.test.js
PASS ./foobar.test.js

Test Suites: 3 passed, 3 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 1.64s
Ran all test suites.


今回の環境

macOS Mojave + Node.js v10.15.3 + Jest 24.7.1

$ node --version

v10.15.3

$ ./node_modules/jest/bin/jest.js --version
24.7.1

$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.14.4
BuildVersion: 18E226


参考資料