TypeScript Advent Calendar 2016 の 最終日 の記事を書かせて頂きました。
はじめに
Javascript のユニットテストを初めてやります。
その上で調査したことを書きます。
ゴール
TypeScript + Webpack + Karma + Jasmine
出力: テストレポート&カバレッジレポート
作業ステップ
- Jasmine 導入
- Karma 導入(Karma + Jasmine)
- Webpack導入(Karma + Webpack)
- TypeScriptの導入(TypeScript + Webpack + Karma)
前提
- npm が使えること
-
node_modules/.bin
にパスが通っていること- ※
npm -g
は使っていません
- ※
- TypeScript を HelloWorld レベルで使えること
- Webpack を HelloWorld レベルで使えること
テスト対象コードの準備
function add(x, y) {
return x + y;
}
こちらをテスト対象にします。
Jasmine導入
Jasmine とは、Javascript のテスティングフレームワークです。
$ npm install -D jasmine
テストコード実装
describe('関数のテスト', function() {
// 成功するテスト
it('1 + 1 = 2である', function(){
expect(add(1, 1)).toBe(2);
});
// 失敗するテスト
it('1 + 2 = 3である', function(){
expect(add(1, 2)).toBe(2);
});
});
Hello World
以下のファイルを HTML で読み込むだけでテスト&テスト結果が表示されるようです。
- node_modules/jasmine-core/lib/jasmine-core/jasmine.css
- node_modules/jasmine-core/lib/jasmine-core/jasmine.js
- node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js
- node_modules/jasmine-core/lib/jasmine-core/boot.js
HTML ファイルの作成
<html>
<head>
<title>Jasmine Test</title>
<link rel="stylesheet" type="text/css" href="./node_modules/jasmine-core/lib/jasmine-core/jasmine.css">
<script src="./node_modules/jasmine-core/lib/jasmine-core/jasmine.js"></script>
<script src="./node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js"></script>
<script src="./node_modules/jasmine-core/lib/jasmine-core/boot.js"></script>
<script src="./src/calc-utils.js"></script>
<script src="./test/calc-utils-test.js"></script>
</head>
<body>
</body>
</html>
ブラウザで開いてみる
※以降 index.html
は使用しません。
Karma導入(Karma + Jasmine)
Node.js 上で動作するテストランナー
AngularJS の開発で使うために作られたらしいです。
テストを実行し、テストレポートを出力したり、カバレッジを取得したりという機能が備わっているようです。
$ npm install -D karma
Hello World
Karma 用の設定ファイル作成
$ karma init
Which testing framework do you want to use ?
Press tab to list possible options. Enter to move to the next question.
> jasmine
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.
> src/*.js
> test/*.js
>
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 "/home/chibi/unit-test/karma.conf.js".
Karma 実行
$ karma start
なんかブラウザが立ち上がった
$ karma start
16 12 2016 13:36:27.571:WARN [karma]: No captured browser, open http://localhost:9876/
16 12 2016 13:36:27.609:WARN [karma]: Port 9876 in use
16 12 2016 13:36:27.614:INFO [karma]: Karma v1.3.0 server started at http://localhost:9877/
16 12 2016 13:36:27.616:INFO [launcher]: Launching browser Chrome with unlimited concurrency
16 12 2016 13:36:27.676:INFO [launcher]: Starting browser Chrome
16 12 2016 13:36:29.778:INFO [Chrome 55.0.2883 (Linux 0.0.0)]: Connected on socket /#_5lnmqR9SDXJe0M6AAAA with id 46430444
Chrome 55.0.2883 (Linux 0.0.0): Executed 0 of 2 SUCCESS (0 secs / 0 secs)
[1AChrome 55.0.2883 (Linux 0.0.0): Executed 1 of 2 SUCCESS (0 secs / 0.002 secs)
[1AChrome 55.0.2883 (Linux 0.0.0) 関数のテスト 1 + 2 = 3である FAILED
Expected 3 to be 2.
at Object.<anonymous> (test/calc-utils-test.js:7:23)
Chrome 55.0.2883 (Linux 0.0.0): Executed 2 of 2 (1 FAILED) (0 secs / 0.004 secs)
[1AChrome 55.0.2883 (Linux 0.0.0): Executed 2 of 2 (1 FAILED) (0.086 secs / 0.004 secs)
なんか上手くいったっぽい
ブラウザが閉じれない(閉じるとまた開く)
karma がウォッチしてて、
コードが変わるたびにテストしてくれるようにしているためですかね。
karma の設定ファイルの以下の部分を true
に変えてあげることで実行を続けることがなくなりました。
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
- singleRun: true,
+ singleRun: true,
PhantomJS
PhantomJS という Headless ブラウザです。
karma start
を実行するたびに Chrome が起動していたので、PhantomJS を使うようにしました。
$ npm install -D karma-phantomjs-launcher
karma.conf.js を編集
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
- browsers: ['Chrome'],
+ browsers: ['PhantomJS'],
実行すると、コンソールログのみに!
JUnit形式レポート出力
$ npm install -D karma-junit-reporter
karma.conf.js を編集
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
- reporters: ['progress'],
+ reporters: ['progress', 'junit'],
+
+ junitReporter: {
+ outputDir: 'report'
+ },
出力結果
$ ls report/
TESTS-PhantomJS_2.1.1_(Linux_0.0.0).xml
$ cat report/TESTS-PhantomJS_2.1.1_\(Linux_0.0.0\).xml
<?xml version="1.0"?>
<testsuite name="PhantomJS 2.1.1 (Linux 0.0.0)" package="" timestamp="2016-12-16T05:34:43" id="0" hostname="Chibintu" tests="2" errors="0" failures="1" time="0.004">
<properties>
<property name="browser.fullName" value="Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1"/>
</properties>
<testcase name="関数のテスト 1 + 1 = 2である" time="0" classname="PhantomJS_2_1_1_(Linux_0_0_0).関数のテスト"/>
<testcase name="関数のテスト 1 + 2 = 3である" time="0.004" classname="PhantomJS_2_1_1_(Linux_0_0_0).関数のテスト">
<failure type="">Expected 3 to be 2.
test/calc-utils-test.js:7:27
loaded@http://localhost:9876/context.js:151:17
</failure>
</testcase>
<system-out>
<![CDATA[
]]>
</system-out>
<system-err/>
カバレッジ出力
$ npm install -D karma-coverage
karma.conf.js を編集
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
+ 'src/*.js': ['coverage']
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
- reporters: ['progress', 'junit'],
+ reporters: ['progress', 'junit', 'coverage'],
junitReporter: {
outputDir: 'report'
},
+
+ coverageReporter: {
+ dir: 'report',
+ reporters: [
+ { type: 'html' },
+ { type: 'cobertura' }
+ ]
+ },
出力結果
$ ls report/
PhantomJS 2.1.1 (Linux 0.0.0) TESTS-PhantomJS_2.1.1_(Linux_0.0.0).xml
$ ls report/PhantomJS\ 2.1.1\ \(Linux\ 0.0.0\)/
cobertura-coverage.xml
$ cat report/PhantomJS\ 2.1.1\ \(Linux\ 0.0.0\)/cobertura-coverage.xml
<?xml version="1.0" ?>
<!DOCTYPE coverage SYSTEM "http://cobertura.sourceforge.net/xml/coverage-04.dtd">
<coverage lines-valid="2" lines-covered="2" line-rate="1" branches-valid="0" branches-covered="0" branch-rate="1" timestamp="1481869473013" complexity="0" version="0.1">
<sources>
<source>/home/chibi/unit-test</source>
</sources>
<packages>
<package name="src" line-rate="1" branch-rate="1" >
<classes>
<class name="calc-utils.js" filename="src/calc-utils.js" line-rate="1" branch-rate="1" >
<methods>
<method name="add" hits="2" signature="()V" >
<lines><line number="1" hits="2" /></lines>
</method>
</methods>
<lines>
<line number="1" hits="1" branch="false" />
<line number="2" hits="2" branch="false" />
</lines>
</class>
</classes>
</package>
</packages>
</coverage>
Webpack導入(Karma + Webpack)
ここでは src/
にファイルが1個しかないので Webpack 効果は薄いけど・・・
$ npm install -D webpack
$ npm install -D karma-webpack
テスト対象コードを変更
Webpack を導入するにあたり、テスト対象コードを module 化します。
module.exports = {
add: function() {
return x + y;
}
}
テストコードを変更
var calcutils = require('../src/calc-utils');
describe('関数のテスト', function() {
// 成功するテスト
it('1 + 1 = 2である', function(){
expect(calcutils.add(1, 1)).toBe(2);
});
// 失敗するテスト
it('1 + 2 = 3である', function(){
expect(calcutils.add(1, 2)).toBe(2);
});
});
webpack.config.js を作成
module.exports = {
entry: "./src/calc-utils.js",
output: {
filename: "bundle.js"
}
};
karma.conf.js を変更
module 化したことによって src/
をロードする必要がなくなりました。
// list of files / patterns to load in the browser
files: [
- 'src/*.js',
'test/*.js'
],
また、Webpack を導入したことによって require
を解決するために karma-webpack
を使用します。
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
- 'src/*.js': ['coverage']
+ 'src/*.js': ['coverage', 'webpack']
+ 'test/*.js': ['webpack']
},
カバレッジが出力できなくなってしまった・・・
$ cat report/PhantomJS\ 2.1.1\ \(Linux\ 0.0.0\)/cobertura-coverage.xml
<?xml version="1.0" ?>
<!DOCTYPE coverage SYSTEM "http://cobertura.sourceforge.net/xml/coverage-04.dtd">
<coverage lines-valid="0" lines-covered="0" line-rate="1" branches-valid="0" branches-covered="0" branch-rate="1" timestamp="1481876370794" complexity="0" version="0.1">
<sources>
<source>/home/chibi/unit-test</source>
</sources>
<packages>
</packages>
</coverage>
こんな感じ・・・
色々調べた結果、2つのパッケージを導入
Karma + Webpack で Coverage を出力する
$ npm install -D karma-sourcemap-loader
$ npm install -D istanbul-instrumenter-loader
karma.conf.js を編集
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
- 'src/*.js': ['coverage', 'webpack'],
- 'test/*.js': ['webpack']
+ 'src/*.js': ['coverage', 'webpack', 'sourcemap'],
+ 'test/*.js': ['webpack', 'sourcemap']
},
+
+
+ // Webpack settings.
+ webpack: {
+ devtool: 'inline-source-map',
+ module: {
+ preLoaders: [
+ {
+ test: /\.js$/,
+ exclude: /(test|node_modules)/,
+ loader: 'istanbul-instrumenter'
+ }
+ ]
+ }
+ },
webpack: {}
の部分は webpack.config.js
の中でも良さそうな気がする
試したらダメだった。 karma.conf.js
に書かないとダメみたい。
karma.conf.js
から webpack.config.js
を参照するわけではなくて、
karma.conf.js
に記述されている webpack
の設定を読むだけのようです。
karma.conf.js を微修正
istanbul-instrumenter-loader
自体がカバレッジに関するパッケージなので、
以下の行を削除してもカバレッジが出力されるようになるっぽい
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
- 'src/*.js': ['coverage', 'webpack', 'sourcemap'],
'test/*.js': ['webpack', 'sourcemap']
},
ここまでで JavaScript + Webpack + Karma + Jasmine
TypeScriptの導入(TypeScript + Webpack + Karma)
$ npm install -D typescript
Webpackを使っているので以下もインストール
$ npm install -D ts-loader
更にKarmaも使っているので以下もインストールしておく
$ npm install -D karma-typescript-preprocessor
更に更に TypeScript で Jasmine のコードを扱うので型定義ファイルをインストールしておく
$ npm install -D @types/jasmine
JavaScriptのコードをTypeScriptに置き換える
src
module.exports = {
add: function() {
return x + y;
}
}
export class CalcUtils {
public static add(x: number, y: number): number {
return x + y;
}
}
test
var calcutils = require('../src/calc-utils');
describe('関数のテスト', function() {
// 成功するテスト
it('1 + 1 = 2である', function(){
expect(calcutils.add(1, 1)).toBe(2);
});
// 失敗するテスト
it('1 + 2 = 3である', function(){
expect(calcutils.add(1, 2)).toBe(2);
});
});
/// <reference path="../node_modules/@types/jasmine/index.d.ts" />
import {CalcUtils} from '../src/calc-utils';
describe('関数のテスト', function() {
it('1 + 1 = 2である', function(){
expect(CalcUtils.add(1, 1)).toBe(2);
});
it('1 + 2 = 3である', function(){
expect(CalcUtils.add(1, 2)).toBe(2);
});
});
tsconfig.json を作成
{
"compileOnSave": true,
"compilerOptions": {
"target": "ES5",
"noImplicitAny": false,
"removeComments": false,
"sourceMap": false
},
"exclude": [
"node_modules",
"test"
],
"files": [
"./src/calc-utils.ts"
]
}
karma.conf.js を変更
// Webpack settings.
webpack: {
devtool: 'inline-source-map',
+ resolve: {
+ extensions: ['', '.ts', '.js', ".tsx"]
+ },
module: {
+ loaders: [
+ {
+ test: /\.ts$/,
+ loader: 'ts-loader'
+ }
+ ],
postLoaders: [
{
- test: /\.js$/,
+ test: /\.ts$/,
exclude: /(test|node_modules)/,
loader: 'istanbul-instrumenter'
}
]
}
},
Karma + Webpack で TypeScript をビルドできるようにするために、
webpack.config.json
に記述するべき TypeScript用の設定を karma.conf.js
に記述する。
Webpack のみでビルドするときは webpack.config.json
にも記述が必要
出力結果
ちゃんと TypeScript のコードでカバレッジが取れている。
最後に
"devDependencies": {
"@types/jasmine": "^2.5.38",
"istanbul-instrumenter-loader": "^1.1.0",
"jasmine": "^2.5.2",
"karma": "^1.3.0",
"karma-chrome-launcher": "^2.0.0",
"karma-coverage": "^1.1.1",
"karma-jasmine": "^1.1.0",
"karma-junit-reporter": "^1.2.0",
"karma-phantomjs-launcher": "^1.0.2",
"karma-sourcemap-loader": "^0.3.7",
"karma-typescript-preprocessor": "^0.3.0",
"karma-webpack": "^1.8.0",
"ts-loader": "^1.3.3",
"typescript": "^2.1.4",
"webpack": "^1.14.0"
}
package.json
のパッケージたちです。
参考にしたURL
https://tech.recruit-mp.co.jp/front-end/post-5299/
http://blog.odoruinu.net/2014/11/27/test-webpack-based-application-with-karma/
http://qiita.com/howdy39/items/cdd5b252096f5a2fa438
ありがとうございました!