JavaScript
mocha
karma
power-assert

JavaScriptのテスト環境構築(Mocha + power-assert + Karma + babel + webpack)

この記事は、初めてJavaScriptのテスト環境を作ってみたおじさんによる、これからJavaScriptのテストを書いていきたいけど、登場人物が多すぎてなにやらめんどくさそうと思っている方に向けた記事兼備忘録です。

初めてのJavaScriptテスト環境構築で一応公式ドキュメントに目を通したけど英語の意味をちゃんと読み取れておらず、ツールやライブラリの理解や使い方が間違っている場合がありますのでアドバイスいただけると幸いです。

各テストツールやライブラリの紹介

JavaScriptのテスト環境を構築するときの一つ目の壁がツールやライブラリがたくさん出てきて、どれを使っていいかわからない/それぞれの役割がわからないことだと思う。

なので、まずはJavaScriptのテスト環境についてググったときに出てくる最近使われてそうな各ツールやライブラリの役割をすごく雑に紹介します。

テストフレームワーク

以下で紹介するフレームワークはどれも見た目がRSpecっぽい。触ったことがあれば雰囲気は簡単につかめるはず。

mocha

Mocha - the fun, simple, flexible JavaScript test framework

  • クライアントサイドとサーバサイドの両方で動作する
  • アサーションやmock/stub機能は持っていないので他のライブラリと組わせて使う

RSpecっぽいBDDスタイルのほかにも, TDD, Exports, QUnit, Require-styleの中から好きなインターフェイスを選択して書ける。テスト結果の出力もかなり柔軟に指定できる感じで猫が走ったりする。ドキュメントを読む限りだと非同期処理のテストも簡単に書けそう。

  describe('#indexOf()', function () {
    it('should return -1 when the value is not present', function () {
      assert.equal(-1, [1,2,3].indexOf(5)); // アサーションは自由に選択化
      assert.equal(-1, [1,2,3].indexOf(2));
    });
  });

Jasmine

Jasmine: Behavior-Driven JavaScript

  • サーバサイドはjasmine-nodeを使う
  • mockやアサーションのライブラリが備わっている
describe("A suite", function() {
  it("contains spec with an expectation", function() {
    expect(true).toBe(true);
  });
});

jest

Jest | Painless JavaScript Unit Testing

  • テストフレームワーク
  • Facebookが作ったJasmineの拡張テストフレームワーク
  • mockやアサーションのライブラリが備わっている

Jasmineとの違いとして読み込むコードは全てMockとして扱われ、テスト対象コードをMockから明示的に除外して使うのが特徴。

jest.dontMock('../sum');

describe('sum', function() {
 it('adds 1 + 2 to equal 3', function() {
   var sum = require('../sum');
   expect(sum(1, 2)).toBe(3);
 });
});

アサーション

アサーションライブラリを自由に選択できるmochaで使う

power-assert

power-assert-js/power-assert: Power Assert in JavaScript. Provides descriptive assertion messages through standard assert interface. No API is the best API.

アサーションライブラリとして説明されること多いが、APIはNode.jsの単純なAssert API。power-assertはテストが失敗したときの出力結果を拡張したもの。テストが失敗したときの情報をたっぷり出力してくれるため、他ライブラリのようにたくさんのアサーションAPIを使い分ける必要がない。

以下のスライドと記事が詳しい

5minで分かるpower-assert
power-assertの使い方 Node.js編 | Web Scratch
power-assertでJavaScriptのテストをする ブラウザ編 | Web Scratch

chai

Chai

should/expect/assertの好きな記法を選択でき、power-assertとは逆にAPIがたくさんある。

mock/stub

アサーションライブラリと同様に自由に選択できるmochaで使う
mock/stubについての理解は以下の記事の解説がわかりやすいかと思います

使えるRSpec入門・その3「ゼロからわかるモック(mock)を使ったテストの書き方」 - Qiita

Sinon

Sinon.JS - Documentation

テストランナー

karma

Karma - Spectacular Test Runner for Javascript
ブラウザ上でテストを走らせるためのツール。mocha等のテストフレームワークとは提供されているプラグインを使って連携する。

どのフレームワークもテストランナーを使わなくてもテストを実行できるが、Karmaを使えば複数のブラウザやデバイス上でのテストが簡単にできる。

またリモートでの実行やテストカバレッジの計測もできるらしい。

ここまでのまとめ

テストフレームワークを使ってテストを書いて、Karmaで走らせる。テストフレームワークにmochaを採用する場合は、アサーションやmockライブラリを別途選択する。

これにプラスしてmodule化されたJavaScriptやbabelをブラウザで使えるようにwebpackやbrowselify等を組み込むことになるかと思います。

次に実際にテスト環境を作って、簡単なテストを実行していきます。

今回の構築するテスト環境の構成

  • Mocha + power-assert + Karma + babel + webpack

power-assertを使ってみたかったのでフレームワークはmochaを採用。Karmaのプリプロセッサには慣れているwebpackを使う。

ゴール

Mochaで書いたコードをwebpackでpower-assert用のコードに変換してkarmaを使ってブラウザ上でテストを走らせる。またbabel(babel-plugin-espower)を使ってテストとテスト対象になる本体のコードをES2015を使えるようにする。

今回はwebpackとbabelを使ったES2015環境の構築の説明は省きます。

以下で作っていくテスト環境のコード
cotto89/sample_js_test: Mocha + power-assert + Karma + babel + webpackを使ったJavaScriptテスト環境のサンプル

ベースになる環境構築

webpack + babelを用いた環境を用意。今回は以下のようなディレクトリ構成で進めていく。

.
├── index.html
├── package.json
├── src
│   └── index.js
├── test
│   └── sample_test.js
└── webpack.config.js

Mochaを導入

npm i -g mocha

package.jsonにtaskを登録

pachage.json
"scripts": { 
  "test": "mocha"
},
smaple_test.js
// mocha公式ドキュメントより拝借
var assert = require('assert');
describe('Array', function() {
  describe('#indexOf()', function () {
    it('should return -1 when the value is not present', function () {
      assert.equal(-1, [1,2,3].indexOf(5));
      assert.equal(-1, [1,2,3].indexOf(1)); // ここが失敗する
    });
  });
});

とりあえず安心するために、テストを走らせ期待通りテストが実行されることを確認する

npm test

default_mocha.png

Karmaを導入

Karma - Installation

karma-cliをインストールしてkarma init。対話形式で使うブラウザやテストフレームワーク、testまでのpath等について聞かれるので適当なものを選択するとkarma.conf.jsが生成される。その際に選択した環境に必要なmoduleは自動でnpmにinstallされる。

npm i -g karma-cli

さらにpreprocessorにwebpackを使うためにkarma-webpackをインストール

npm i -D karma-webpack

preprocessorはkarmaの前処理で、webpackでブラウザで動くコードに変換されてKarmaにコードが渡る

webpack/karma-webpack: Use webpack with karma.

とりあえず安心するために、Karmaを走らせて期待通りに動作することを確認する

karma.conf.js
...
preprocessors: {
    // add webpack as preprocessor
    'test/*_test.js': ['webpack'],
    'test/**/*_test.js': ['webpack']
},
...
pachage.json
"scripts": { 
  "test": "karma start"
},
npm test

これでmochaで書いたテストがwebpack経由でkarmaに渡されてtestがブラウザ上で実行されることが確認できた

power-assertを導入

power-assert-js/power-assert

power-assertとその他に必要なmoduleをインストール

power-assertとテストコードもES2015で記述できるようにbabel-plugin-espowerを導入する

webpack-espower-loader does not work with babel-loader!

webpack-espower-loader does not work with babel-loader due to the change of transpiled code since babel 5.0. Please use babel-plugin-espower with babel-loader.

power-assert-js/webpack-espower-loader: Power Assert instrumentor module for webpack

とのことなのでpower-assert + babel + webpackを使う場合は、webpack-espower-loaderは使わずにbabel-plugin-espowerを使う

またCannot find module Error · Issue #2 · power-assert-js/webpack-espower-loaderの理由でjson-loaderもインストールする

npm i -D power-assert babel-plugin-espower json-loader

karma.conf.jsを編集

プリプロセッサで利用するwebpackをsetup

karma.conf.js
...
webpack: {
  devtool: 'inline-source-map',
  module: {
    loaders: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        query: {
          presets: ['es2015'],
          plugins: ['babel-plugin-espower']
        }
      },
      {
        test: /\.json$/,
        loader: 'json'
      }
    ]
  }
},
...

動作確認

テストを含めてES2015で書いたコードを書いて動くことを確認。

適当なテスト対象となるコードを用意

cat.js
export default class Cat {
  constructor(name) {
    this.name = name;
  }

  greet() {
    return `Nyaaaan! I am ${this.name}`
  }
}

ES2015でテストを書いてみる

sample_test.js
import assert from 'power-assert'
import Cat from '../src/cat'

describe('Cat', () => {
  var tama;
  beforeEach(() => {
    tama = new Cat('tama')
  });

  describe("greet()", () => {
    it("挨拶をする", () => {
      assert.equal(tama.greet(), 'Nyaaaan! I am neko');
    });
  });
});

power-assert.png

問題なくテストが実行され、power-assertによる出力を確認することができた

まとめ

細かいことはわからないけどテスト環境を作ってとりあえず動くところまではできた。一度手を動かして各ツールやライブラリの役割を把握しておくことで、他の構成で組む場合でも全体像を把握できているのでわりと楽に組めるのではと思う。

あとは各ツールやライブラリのドキュメントを読んで細かいオプションを指定するなりどんどん使いやすいようにカスタマイズしていく。

とりあえず動くとこまではきたが、webpack + babel + power-assertあたりの設定にこれでいいのか不安があるので間違っていたらアドバイスください。

cotto89/sample_js_test: Mocha + power-assert + Karma + babel + webpackを使ったJavaScriptテスト環境のサンプル