最近はTypeScriptを使ってフロントエンドの開発をしている。
- JSをbrowserifyでビルドし、ライセンスコメントを適切に残す - $shibayu36->blog;
- gulp + browserify + tsifyを利用してTypeScriptコンパイル環境を作る - $shibayu36->blog;
- typingsを使ってnpmモジュールをTypeScriptで利用する - $shibayu36->blog;
以上の記事に引き続き、次は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"; }
前提
どのようにやっていくか
Mochaを導入する
ひとまずテストフレームワークとしてMochaを導入する。他にもJasmineなどが同じような用途のツールとして存在するが、ダウンロード数などや最近の流行りなどから適当に判断してMochaを採用した。
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としていろんな処理を追加することが出来る。
これを使うことで、今回であれば以下のことを実現できる。
インストール
以下のコマンドで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つずつシンプルに使ってみると概念が理解できるのでおすすめ。
参考
- JSをbrowserifyでビルドし、ライセンスコメントを適切に残す - $shibayu36->blog;
- gulp + browserify + tsifyを利用してTypeScriptコンパイル環境を作る - $shibayu36->blog;
- typingsを使ってnpmモジュールをTypeScriptで利用する - $shibayu36->blog;
- https://github.com/shibayu36/typescript-project-sample/tree/967c9d74fd3d844127a4352f19a0cc31b7a0690d:title=サンプルコード
- https://mochajs.org/
- http://chaijs.com/
- https://karma-runner.github.io/0.13/index.html
- https://www.npmjs.com/package/karma-chrome-launcher
- https://www.npmjs.com/package/karma-browserify
【おまけ】今回関係するファイルの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