ユニットテストツール「Jest」の使い方

Facebookがオープンソースとして開発しているJestを利用して、ユニットテスト行う方法について確認します。

Jestとは

Facebookがオープンソースとして開発している ユニットテストツール です。

ユニットテストツールはJest以外にも以下のようなツールが存在します。

分類 概要
テストランナー テスト実行環境、検証結果のレポート機能などを提供 Karma
テストフレームワーク describe it などテストの構造を作る機能を提供 Mocha
アサーション テスト結果が期待通りであるか判定する機能を提供 Chai
テストユーティリティ モック、スタブなどの機能を提供 Sinon

Jestは上記機能をオールインワンで提供しているので、導入の負担が低いのが魅力です。

簡単なテストで動作確認

まず、簡単なテストをJestで実行させるところまで確認します。

プロジェクト作成

パッケージ管理に yarn を利用します。 yarn initpackage.json を作成します。

$ yarn init
$ cat package.json
{
  "name": "test1",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT"
}

Jestをインストール

$ yarn add --dev jest

Jestがインストールされました。

$ cat package.json
{
  "name": "test1",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "devDependencies": {
    "jest": "^23.6.0"
  }
}

テスト対象ファイル実装

sum.js というファイルを作成し、以下処理を記述します。

function sum(a, b) {
    return a + b
}
module.exports = sum

テストコード実装

sum.test.js というファイルを作成し、以下処理を記述します。

const sum = require('./sum')

test('adds 1 + 2 to equal 3', () => {
    expect(sum(1, 2)).toBe(3)
})

以下の操作を行なっています。

  • テスト対象ファイルを読み込み
  • test関数
    • 第1引数にテストの概要を記述
    • 第2引数にテストを記述
  • expect関数
    • 引数にテスト対象の処理を記述
    • マッチャー( toBe toEqual など)で期待する動作を検証
「test関数」と「it関数」
ここではtest関数を利用しましたが、it関数でも同じ動作をします。
「describe関数」でテスト対象をカテゴライズ
ここでは利用しませんでしたが、describe関数を利用すると複数のテストをグルーピングできます。テストのカテゴライズができるので、テストの管理に役立ちます。

https://jestjs.io/docs/en/api#describename-fn

ファイル構成

ここまでの作業で、以下のようなファイル構成になりました。

.
├── node_modules/
├── package.json
├── sum.js
├── sum.test.js
└── yarn.lock

テスト実行

yarnコマンド 経由でテストを実行できるように、package.jsonscripts を以下のように記述します。

$ cat package.json
{
  "name": "test1",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "devDependencies": {
    "jest": "^23.6.0"
  },
  "scripts": {
    "test": "jest"
  }
}

テストを実行します。

$ yarn test
yarn run v1.9.4
$ jest
 PASS  ./sum.test.js
  ✓ adds 1 + 2 to equal 3 (6ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.227s
Ran all test suites.
✨  Done in 3.50s.

簡単なテストではありましたが、Jestを利用してテストを実行することができました。

Jestコマンドのオプション

coverage

coverageオプション を利用するとテストカバレッジを確認できます。

$ yarn test --coverage
yarn run v1.9.4
$ jest --coverage
 PASS  ./sum.test.js
  ✓ adds 1 + 2 to equal 3 (12ms)

----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |      100 |      100 |      100 |      100 |                   |
 sum.js   |      100 |      100 |      100 |      100 |                   |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.319s
Ran all test suites.
✨  Done in 3.59s.

coverageオプション を利用した場合、テストを実行すると coverageフォルダ が生成されます。

coverage/lcov-report/index.html をブラウザで開くと詳しいカバレッジ情報を確認できます。

$ open coverage/lcov-report/index.html
525-javascript-jest_coverage.png

watch, watchAll

525-javascript-jest_watch.gif

watchオプション watchAllオプション を利用すると、ファイルの変更を監視してテストを実行してくれます。

その他

下記ページにてコマンドオプションについて確認できます。

https://jestjs.io/docs/en/cli

主なMatcher

利用頻度の高いMatcherを紹介します。

it('matchers', () => {
    // 等しい
    expect(1 + 5).toBe(6)

    // notで「~でない」の判定ができます。
    expect(1 + 5).not.toBe(7)

    // toBeは厳密な等価性を判定するので、オブジェクトの場合はtoEqualを利用します。
    expect({ a: 100 }).toEqual({ a: 100 })

    // 大きい
    expect(4).toBeGreaterThan(3)
    expect(4).toBeGreaterThanOrEqual(4)

    // 小さい
    expect(4).toBeLessThan(5)
    expect(4).toBeLessThanOrEqual(4)

    // null, true, false
    expect(null).toBeNull()
    expect(true).toBeTruthy()
    expect(false).toBeFalsy()

    // 文字列
    expect('abcdefg').toMatch(/bc/)
    expect('abcdefg').not.toMatch(/bd/)

    // 配列
    expect([1, 2, 3]).toContain(2)
})

下記ページでより詳しい使い方を確認できます。

モックの利用方法

モックの利用方法を紹介します。

const myMethod = (cnt, callback) => {
    let total
    while (cnt) {
        total = callback(cnt)
        cnt--
    }
    return total
}

it('mock', () => {
    // arrange
    const mockCallback = jest.fn(x => x * 2)

    // act
    myMethod(3, mockCallback)

    // assert
    // モックメソッドが3回呼ばれたこと
    expect(mockCallback.mock.calls.length).toBe(3)

    // モックメソッドが受け取った引数
    expect(mockCallback.mock.calls[0][0]).toBe(3)  // 1回目 第1引数
    expect(mockCallback.mock.calls[1][0]).toBe(2)  // 2回目 第1引数
    expect(mockCallback.mock.calls[2][0]).toBe(1)  // 3回目 第1引数

    // モックメソッドの戻り値
    expect(mockCallback.mock.results[0].value).toBe(6)  // 1回目
    expect(mockCallback.mock.results[1].value).toBe(4)  // 2回目
    expect(mockCallback.mock.results[2].value).toBe(2)  // 3回目
})

詳しい利用方法は以下ページで確認できます。

https://jestjs.io/docs/en/mock-functions

axiosのAPI呼び出しをモックに差し替えるなどの利用方法が解説されています。

設定調整

調整方法

以下の方法で設定を調整できます。

  • package.jsonファイル に設定を記述
  • jest.config.jsファイル を作成して、設定を記述
  • テスト実行時にオプションで設定を調整

詳しい設定方法などは以下ページで確認できます。
https://jestjs.io/docs/en/configuration

testRegexでテストファイルを調整

Jestはデフォルトだと以下のファイルをテストファイルとみなします。

  • *.test.js
  • *.spec.js
  • __tests__ ディレクトリ以下のファイル

変更したい場合は、 testRegex の設定を調整します。
https://jestjs.io/docs/en/configuration#testregex-string

参考