$shibayu36->blog;

クラスター株式会社のソフトウェアエンジニアです。エンジニアリングや読書などについて書いています。

Karma, Mocha, Chaiを使ってTypeScriptでのテスト環境を構築する

最近はTypeScriptを使ってフロントエンドの開発をしている。

以上の記事に引き続き、次はTypeScriptでユニットテストをするためのシンプルな環境を構築してみた。

今回のサンプルコードは https://github.com/shibayu36/typescript-project-sample/tree/967c9d74fd3d844127a4352f19a0cc31b7a0690d においてあるので、見ながら確認してもらえると良さそう。

やりたいこと

フロントエンドをTypeScriptで開発しているので、ユニットテスト自体もTypeScriptで書きたい。またテスト自体はCLIでコマンドを叩くことで実行し、結果を見れるようにしたい。最終的には以下のようなTypeScriptで書かれたテストファイルをCLIのコマンドでテスト出来るようになれば良い。


TypeScriptで書かれたテストファイル src/ts/test/module1.ts

/// <reference path="../../../typings/browser.d.ts" />

"use strict";

import { assert } from "chai";
import module1 from "../module1";

describe("module1 default function", () => {
    it("returns module1", () => {
        let expect: string;
        expect = 'module1';
        assert.equal(module1(), expect);
    });
});

TypeScriptで書かれた実装ファイル src/ts/module1.ts

export default function (): string {
    return "module1";
}

前提

  • 以前の記事で書いたとおりbrowserifyやtsifyを使ったTypeScriptのビルド環境は一通りできているとする(ツールのインストールは終わっている)
  • また型管理のためにtypingsが導入できているとする
  • 今回はそれに追加してTypeScriptでテストを書き、CLIで実行できるようにしたい

どのようにやっていくか

  • テストフレームワークとして、Mochaを利用する
  • Assertionライブラリとして、Chaiを利用する
  • テスト実行環境として、またテスト前にビルドなどを行ってくれるツールとしてKarmaを利用する
  • これらを組み合わせて、TypeScriptでテストを書いてCLIで実行できるようにする

Mochaを導入する

ひとまずテストフレームワークとしてMochaを導入する。他にもJasmineなどが同じような用途のツールとして存在するが、ダウンロード数などや最近の流行りなどから適当に判断してMochaを採用した。

https://mochajs.org/

Mochaを導入することでテストを書く時にdescribeとかitなどのテストを書くための標準的なAPIを利用することができるようになる。またMochaはテストを実行することもできるが、そのあたりはKarmaを使ったほうがTypeScriptをテスト前にコンパイルするなど便利なことができるので、その機能は利用しない。

Mochaの導入自体はnpmでインストールして、TypeScriptのために型ファイルを持ってくれば良いだけ。以下のコマンドで終わり。

npm install mocha --save-dev
$(npm bin)/typings install mocha --ambient --save

Chaiを導入する

Mochaはテストフレームワークで、それにAssertionライブラリを組み合わせることで実際にテスト出来る。この辺は正直どこまでがどのツールの役割なのか少しわかりづらいんだけど、とりあえずMochaだけだとテストを書けないのでAssertionライブラリを導入する。

https://mochajs.org/#assertions に書いてあるとおり、MochaではAssertionライブラリは好きに組み合わせられる。assertを使っても良いし、power-assertを使っても良いしなんでも良い。こちらもダウンロード数などから見てなんとなくChaiというのを使ってみた。

これも導入はインストールして型ファイルを持ってくるだけ。

npm install chai --save-dev
$(npm bin)/typings install chai --ambient --save

Karmaを導入する

MochaとChaiのインストールができたので、テスト実行環境のためにKarmaを導入し、これらと組み合わせる。KarmaというのはJSのテストの実行をいい感じにやってくれるツール。また、それに合わせてテスト実行前にpreprocessorとしていろんな処理を追加することが出来る。

これを使うことで、今回であれば以下のことを実現できる。

  • テスト実行前にTypeScriptをコンパイルできるようにする
  • CLIでテストを実行できるようにする

インストール

以下のコマンドでnpmでインストールする。Karmaはテストファイル内でAPIを使いたいわけではないので、型定義ファイルは必要ない。またJS実行はChrome環境を利用しようと思うので、karma-chrome-lauhcher を利用する。

npm install karma --save-dev
npm install karma-chrome-launcher --save-dev

karma.conf.jsの雛形を作る。

その後セットアップ。コマンドを実行して適当に質問に答えるだけ。

$(npm bin)/karma init

Which testing framework do you want to use ?
Press tab to list possible options. Enter to move to the next question.
> mocha

Do you want to use Require.js ?
This will add Require.js plugin.
Press tab to list possible options. Enter to move to the next question.
> no

Do you want to capture any browsers automatically ?
Press tab to list possible options. Enter empty string to move to the next question.
> Chrome
>

What is the location of your source and test files ?
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
Enter empty string to move to the next question.
>

Should any of the files included by the previous patterns be excluded ?
You can use glob patterns, eg. "**/*.swp".
Enter empty string to move to the next question.
>

Do you want Karma to watch all the files and run the tests on change ?
Press tab to list possible options.
> yes


Config file generated at "/Users/shibayu36/development/src/github.com/shibayu36/typescript-project-sample/karma.conf.js".

今回はテストフレームワークにMochaを使いたいのでmochaを選択、またひとまずJSのテスト実行にはデフォルトのChrome環境を使うことにした。これでひとまずKarmaの設定ファイルであるkarma.conf.jsの雛形ができた。

テストファイルの場所を指定する

とりあえずkarma.conf.jsにテストファイルの場所を指定する。テストファイルはsrc/ts/test/以下に配置しようと思うので、filesという設定にそのように記述する。

    files: [
        'src/ts/test/**/*.ts'
    ],

テスト実行前にTypeScriptをコンパイルするようにする

preprocessorという仕組みを利用すればテスト実行前にTypeScriptをコンパイル出来る。preprocessorとbrowserifyとtsifyを使えば簡単にTypeScriptのコンパイル環境をKarmaに組み込める。

今回はkarma-browserify とtsifyを使う。

まず必要なモジュールをインストールする。browserifyやtsifyは入っている前提で。

npm install karma-browserify --save-dev
# peerDependenciesでwatchfyも必要って言われるので入れる
npm install watchify --save-dev

karma.conf.jsに設定を入れる。

  • frameworksにbrowserifyを追加
  • preprocessorsにbrowserifyを追加
  • browserifyでTypeScriptをコンパイルするような設定を入れる

という三つを行うだけ。詳しくは https://www.npmjs.com/package/karma-browserify を参照。

    frameworks: ['browserify', 'mocha'],
    ...
    preprocessors: {
        'src/ts/**/*.ts': ['browserify']
    },
    ...
    browserify: {
        plugin: ['tsify'],
        extensions: ['.ts']
    }

ここまでのまとめ

最終的にkarma.conf.jsは以下のようになる。

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',


    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['browserify', 'mocha'],


    // list of files / patterns to load in the browser
    files: [
        'src/ts/test/**/*.ts'
    ],


    // list of files to exclude
    exclude: [
    ],


    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
        'src/ts/**/*.ts': ['browserify']
    },


    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress'],


    // web server port
    port: 9876,


    // enable / disable colors in the output (reporters and logs)
    colors: true,


    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,


    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,


    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome'],


    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity,

    browserify: {
        plugin: ['tsify'],
        extensions: ['.ts']
    }
  })
}

ここまででKarmaとMochaを組み合わせ、テスト実行前にTypeScriptをコンパイルして、そのままテストを実行出来るようになった。あとはテストを書くだけである。

テストを書く

今回は一番シンプルに文字列を返すだけの関数のテストを行う。

まず実装ファイルは単にmodule1という文字を返すだけの実装とする。

src/ts/module1.ts

export default function (): string {
    return "module1";
}

そしてこれに対してテストを書く。このテストがどうしてこのように書けるかはコメントで書いておいたので参照されたい。

src/ts/test/module1.ts

// typingsでインストールした型定義ファイルにreference
/// <reference path="../../../typings/browser.d.ts" />

"use strict";

// chaiのassertを利用する
import { assert } from "chai";

// src/ts/module1.tsのテストをしたい
import module1 from "../module1";

// describeやitはMochaが提供。
// またKarmaの設定でframeworkに指定しているのでimport等せずに使える。
describe("module1 default function", () => {
    it("returns module1", () => {
        // TypeScriptでテストをかける
        let expect: string;
        expect = 'module1';
        // chaiのAPIであるassertが利用可能
        assert.equal(module1(), expect);
    });
});

テストを実行する

ここまで来たらテストを実行するだけである。以下のコマンドでテストを実行できる。

$(npm bin)/karma start
11 04 2016 23:11:54.247:INFO [framework.browserify]: registering rebuild (autoWatch=true)
11 04 2016 23:11:56.682:INFO [framework.browserify]: 208191 bytes written (0.43 seconds)
11 04 2016 23:11:56.684:INFO [framework.browserify]: bundle built
11 04 2016 23:11:56.686:WARN [karma]: No captured browser, open http://localhost:9876/
11 04 2016 23:11:56.690:INFO [karma]: Karma v0.13.22 server started at http://localhost:9876/
11 04 2016 23:11:56.701:INFO [launcher]: Starting browser Chrome
11 04 2016 23:11:58.047:INFO [Chrome 49.0.2623 (Mac OS X 10.10.5)]: Connected on socket /#_xLUiYHd2JHCRHzNAAAA with id 53578114
Chrome 49.0.2623 (Mac OS X 10.10.5): Executed 1 of 1 SUCCESS (0.011 secs / 0 secs)

見たとおり、「Executed 1 of 1 SUCCESS」という表示が出たので、テストを通すことができた。デフォルトでautoWatchが有効になっているので、テストファイルや実装ファイルが変更されるたびにテストが実行される。

まとめ

今回はTypeScriptでテストを書くためのシンプルな環境(シンプルと言えるかは分からないけど...)を作成してみた。ツールはいろいろあるけれどひとまず1つずつシンプルに使ってみると概念が理解できるのでおすすめ。

【おまけ】今回関係するファイルのtree構造

.
├── karma.conf.js
├── package.json
├── src
│   └── ts
│       ├── app.ts
│       ├── module1.ts
│       ├── module2.ts
│       └── test
│           └── module1.ts
├── typings
│   ├── browser
│   │   └── ambient
│   │       ├── chai
│   │       │   └── index.d.ts
│   │       └── mocha
│   │           └── index.d.ts
│   └── browser.d.ts
└── typings.json