Help us understand the problem. What is going on with this article?

フロントエンドにテストを導入

More than 3 years have passed since last update.

2016-8-8
※webpack単体の記事を書きました。よろしければこちらもどうぞ step by stepで始めるwebpack


2016-5-16
※karma単体の記事を書きました。よろしければこちらもどうぞ step by stepで始めるKarma


本記事は画面のJavaScriptのテストとかまったくやったことない方
Mocha?webpack?karma?それぞれの解説記事はよく見るけど全体像がよくわからんという方向けです。(数日前の自分です)
全体を通して導入の流れを解説した記事があると全体像が理解しやすいのではと思い書いてみました。

前提

Nodejs,npm,chromeが導入済みであること

流れ

Step 表題 目的
Step 0 テスト対象アプリケーションを作成
Step 1 ライブラリを使わないでテスト ブラウザのconsole機能でテストを行う
Step 2 Mochaを導入 テスト結果をリッチにする
Step 3 Nodejsでテストを実行 ブラウザの世界からNodejsの世界にテストを持って行き、後続のStepへつなげる
Step 4 webpackを導入 ブラウザ上でモジュールの読み込みができるようにする
Step 5 assertを導入 テストコードのアサーションを最適化する
Step 6 karmaを導入 ※step by stepで始めるKarma ブラウザ上でテストを実行する

リンク先はソースです。

ゴール

Screen Shot 2016-05-07 at 8.39.23 AM.png

以後Stepの最後にいれていますが
青背景が実行関連のファイル
緑背景がテスト関連のファイル
赤背景がStepごとの差分です。

Step 0 テスト対象アプリケーションを作成

テキストボックスが2つあり計算ボタンを押すと加算して表示するアプリケーションを作成します。

index.html
<body>
    <div>
        <input type="number" id="value1"> + <input type="number" id="value2"> = <span id="result"></span><br>
        <input type="button" id="calc" value="計算"></input>
    </div>
    <script src="/src/calc-util.js"></script>
    <script src="/src/index.js"></script>
</body>
index.js
document.getElementById('calc').addEventListener('click', function () {
    var value1 = Number(document.getElementById('value1').value);
    var value2 = Number(document.getElementById('value2').value);

    var result = add(value1, value2);
    document.getElementById('result').innerText = result;
});

これがテスト対象のコードになります。

calc-util.js
function add(value1, value2) {
    return value1 + value2;
}

 width=

Step 1 ライブラリを使わないでテスト

テスト実行用のhtmlを適当に作ってブラウザで表示、デベロッパーツールでログを見るだけの形です。

spec.html
<body>
    <script src="/src/calc-util.js"></script>
    <script src="/test/calc-util-spec.js"></script>
</body>
calc-util-spec.js
console.info('add関数のテスト');
console.info('1+2は3である');

if (add(1, 2) === 3) {
    console.info('成功')
} else {
    console.error('失敗')
}

Screen Shot 2016-05-04 at 9.59.32 AM.png

Step 2 Mochaを導入

前回の状態で一応テストとしては成り立ってはいますが、コンソールエラーを探すとか辛いです。
その問題を解決するために MochaJasmineというテストフレームワークを使います。
今回はMochaを使ってみます。

テストコードを修正

describeでまとめたりitで検証を定義。
成功だけではつまらないので失敗ケースもついでに追加。
※テスト自体が間違っているだけですが、シンプルにしたいのでm(_ _)m

calc-util-spec.js
describe('add関数のテスト', function() {
    it('1+2は3である', function() {
        if (add(1, 2) === 3) {
        } else {
            throw new Error('失敗');
        }
    });
    it('1+2は4である', function() {
        if (add(1, 2) === 4) {
        } else {
            throw new Error('失敗');
        }
    });
});

テスト実行用htmlを修正

css・jsの読み込みやセットアップ処理、出力結果の場所などを追加しています。

spec.html
<head>
    <meta charset="UTF-8">
    <title>Step 2 spec</title>
    <link href="https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.css" rel="stylesheet" />
</head>
<body>
    <div id="mocha"></div>

    <script src="https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.js"></script>
    <script>mocha.setup('bdd')</script>

    <script src="/src/calc-util.js"></script>
    <script src="/test/calc-util-spec.js"></script>

    <script>mocha.run();</script>
</body>

実行結果

かっこいい感じの表示になりました。
これならエラーを見つけるのは簡単ですね。
 width=

 width=

Step 3 Nodejsでテストを実行

修正の都度ブラウザで実行するのも辛いです。
またCIを行うためにもブラウザを使わずにNodejsで実行できるようにする必要があります。
Mochaはnpmでインストールすることでコンソール上から実行が可能になります。

package.jsonを作成

package.json(設定ファイル)を作りたいだけなの返答は適当でOKです

npm init

Mochaをインストール

npm i -D mocha

テストコードを修正

テスト対象コードを読み込むためにrequireで指定します。

calc-util-spec.js
var calcUtil = require('../src/calc-util.js');

describe('add関数のテスト', function() {
    it('1+2は3である', function() {
        if (calcUtil.add(1, 2) === 3) {
        } else {
            throw new Error('失敗');
        }
    });
    it('1+2は4である', function() {
        if (calcUtil.add(1, 2) === 4) {
        } else {
            throw new Error('失敗');
        }
    });
});

テスト対象コードを修正

requireで読み込むには以下のようにmodule.exportで括ります。
※実際に動かす画面が動かなくなりますがこれは次のstepで解消します。まずはテストを通します。

calc-util.js
module.exports = {
    add : function (value1, value2) {
        return value1 + value2;
    }
}

package.jsonを修正

以下のようにtestをmochaに修正し、npm testで実行できるようにします。
※テスト対象のデフォルトがtest/*.jsらしいのでSpecファイルの指定は不要です。

package.json
  "scripts": {
    "test": "mocha",

テスト実行

npm test

 width=

 width=

Step 4 webpackを導入

requireできるようにadd関数をいじってしまったので画面が動かなくなってしまいました。
これを解決するのが webpackbrowserify といったツールを使用します。
今回はwebpackを使用してみます。

webpackをインストール

npm i -D webpack

configファイルを作成

設定の詳細な意味等はこちらの記事webpack入門が分かりやすかったので参照してください。
ざっくり書くとindex.jsとrequireされているcalc-util.jsをapp.bundle.jsにまとめる設定になっています。

touch webpack.config.js
webpack.config.js
module.exports = {
    entry: {
        app: './src/index.js'
    },
    output: {
        path: './dist',
        filename: '[name].bundle.js'
    }
};

package.jsonを修正

以下のようにbuildを追加し、npm run buildで実行できるようにします。
※ webpackとうつだけでデフォルトでwebpack.config.jsが使用されます。

package.json
  "scripts": {
    "test": "mocha",
+   "build": "webpack",

add関数を呼んでいるjsを修正

テストコードと同じ様にrequireを使って以下のようにします

index.js
var calcUtil = require('./calc-util.js');

document.getElementById('calc').addEventListener('click', function () {
    var value1 = Number(document.getElementById('value1').value);
    var value2 = Number(document.getElementById('value2').value);

    var result = calcUtil.add(value1, value2);
    document.getElementById('result').innerText = result;
});

webpackを実行

npm run build

これでapp.bundle.jsが作成されブラウザで読めるようになりました。

読み込むhtmlを修正

もともと2つ読み込んでいたjsが1つになります。

index.html
<body>
    <div>
        <input type="number" id="value1"> + <input type="number" id="value2"> = <span id="result"></span><br>
        <input type="button" id="calc" value="計算"></input>
    </div>
<!--    <script src="/src/calc-util.js"></script> -->
<!--    <script src="/src/index.js"></script> -->
    <script src="/dist/app.bundle.js"></script>
</body>

副次的にadd関数がグローバル汚染しなくなるというメリットも生まれました。

Screen Shot 2016-05-07 at 8.38.56 AM.png

Step 5 assertを導入

テストをもっと楽にしましょう。
ifやthrowが冗長ですよね。それを可能にするのがassertです。
Nodejsに標準に付属しているライブラリのようです。

require('assert')を行いassert()でテスト対象を指定します。

calc-util-spec.js
var assert = require('assert');
var calcUtil = require('../src/calc-util.js');

describe('add関数のテスト', function() {
    it('1+2は3である', function() {
        assert(calcUtil.add(1, 2) === 3);
    });
    it('1+2は4である', function() {
        assert(calcUtil.add(1, 2) === 4);
    });
});

だいぶすっきりしました。

 width=

Screen Shot 2016-05-07 at 8.39.06 AM.png

Step 6 karmaを導入

今まではNodejsの世界でテストをしていました。
しかしブラウザによって動かない関数を使っていたらエラーになってしまいます。
それを解決するためにkarmaというブラウザ上でテストを動かすライブラリを使います。

karma関連のモジュールをインストール

npm i -D karma karma-chrome-launcher karma-mocha karma-webpack

karmaが本体です。それ以外がプラグインとなっており
karma-chrome-launcherがchrome起動用。
karma-mochaがmocha用。
karma-webpackがwebpack用です。

2016-05-08 追記
karma-cliのグローバルインストールが必要でした。

npm i -g karma-cli

初期設定ファイルを作成

コマンドを叩き質問にこたえることで簡単に設定ファイル(karma.conf.js)を作ることができます。

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.
> test/**/*spec.js
18 04 2016 20:26:02.739:WARN [init]: There is no file matching this pattern.

> 

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

Require.jsは必要そうに感じるのですが必要ないです。
※karma-webpackでrequireを変換したファイルを食わせるためです。

karma.conf.jsを修正

上記のまま後述のkarma startを実行するとUncaught ReferenceError: require is not definedと出ます。
ブラウザ上で動かしているためrequireが見つからないということですね。
Step 4でwebpackを通してrequireを変換した時と同じことをする必要があります。
karmaではpreprocessorsの設定でwebpackを通すように指定することができます。

karma.conf.js
    preprocessors: {
+       'test/**/**spec.js': ['webpack']
    },

package.jsonを修正

npm testで実行できるようにするため修正します。

package.json
-   "test": "mocha",
+   "test": "karma start",

karmaを起動

npm test

 width=

Chromeが立ち上がりターミナル上でテスト結果が表示されています。
設定ファイルを作った時の最後の質問でtrueとしているのでテストファイルやテスト対象ファイルを修正した際に、自動でテストが走ります。
テストやソースを修正してからブラウザ画面をF5で更新して確認という手間が省けますね。

Screen Shot 2016-05-07 at 8.39.13 AM.png

余談

図はdraw.ioで1ファイル内で全て作ってpngエクスポートしてそれぞれ範囲選択キャプチャしてます。
良いやり方をご存知でしたらご教示くださいm(_ _)m

howdy39
heyinc corporate engineer https://techthetoaster.booth.pm/
https://howdy39.dev/
storesjp
インターネットビジネスの企画・開発・運営、マーケティング、プロモーション、コンテンツの企画・制作
https://about.stores.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした