JavaScriptテスト自動化キホンのキ

[ディップ Advent Calendar](http://qiita.com/advent-calendar/2016/dip-dev) の3日目です。
※トランスパイラ項にECMAScriptについて追記しました。(12/5)

## 概要
自分は普段バックエンド側を担当しており、最近になってJavaScriptのテスト自動化について勉強を始めました。
そこで、自分と同じような**JavaScriptテスト自動化の初心者の方**に向けて、自分が一通りの環境を構築する中で得た内容を共有したいと思います。
キホンのキということで、導入にあたっての前提知識のような内容を主に記述します。

## JavaScriptテスト自動化の特徴
特に目立つポイントとして**構成要素が多い**ことが挙げられます。
Webで記事を探してみると
「○○と▲▲と□□と◆◆を使ったJavaScriptのテスト自動化！」
みたいな色々なものを組み合わせたタイトルの記事が多く見つかります。
これらについて前提知識が無いと「○○」や「▲▲」が何なのか分からず、`ヤサイマシマシアブラカラメ`だったり、
`トールアイスライトアイスエクストラミルクラテ`のような謎の呪文に見えてしまいます。
そこで、これらの謎の呪文の構成要素について解説をしていきます。

## テスト自動化における構成要素
### 1. テストフレームワーク
※ 皆さんご存知の内容かと思いますが、改めて書いています。

テスト自動化にあたり、我々はテストコードを書く必要があります。
しかし、何も道具が無い状態ではテストコードを書くにも多大な労力が伴います。
例えば以下のような足し算をする関数があった場合、

```js
function add(x, y) {
  return x + y;
}
```
これに対するテストコードを自力で書くとしたら

```js
if(add(1, 2) == 3) {
  console.log("テスト結果OK");
} else {
  console.log("テスト結果NG");
}
```
のように、ひたすら泥臭いコードを書くハメになる可能性があります。（これは極端な例ですが）
また、このままではテスト実行後の結果がとても分かり辛いです。
どんなテストに成功したのか/失敗したのか、全体でどれだけテストがあって、どれだけパスしたのか。
はたまた失敗した場合に、どんな部分がNGだったのか。

これらを解決するためにあるのが、**テストフレームワーク**と呼ばれるものです。
Javaで言えば[JUnit](http://junit.org/junit4/)、Rubyでは[RSpec](http://rspec.info/)、PHPでは[PHPUnit](https://github.com/sebastianbergmann/phpunit)や[PHPSpec](http://www.phpspec.net/en/stable/)などが該当します。

こういったテストフレームワークを使うことで、テストを**簡潔に分かりやすく**書けるようになります。
また、こんな感じでテスト結果も見やすく出力してくれます。
（下図はRSpecでのテスト実行結果の様子です）
![RSpecの実行結果](https://qiita-image-store.s3.amazonaws.com/0/72466/a9d2be4d-f87b-49cb-40a6-5d0261b26723.png)

テストの成否具合や実施内容、問題となる内容が詳しく出力されていますね。

さて、このテストフレームワークですが、JavaScriptにも当然あります。
よく見かけるものでは、**[Mocha](https://mochajs.org/)**や**[Jasmine](https://jasmine.github.io/)**が挙げられます。
これらのテストフレームワークを使用することで、JavaScriptでも効率的にテストを書くことができます。

#### ※テストフレームワークのお供その１（テストダブル）
便利なテストフレームワークですが、痒いところに手が届かないケースが出てきます。
例えば、ある関数`funcA()`からさらに呼び出される別の関数`funcB()`について、適切に呼び出されたかを確認するような複雑なケースをテストしたい場合、Mochaだけでは上手くテストできません。

```js
function funcA() {

  // 色んな前処理前処理
  // ここでarg1からarg3までが用意される
  ...

  funcB(arg1, arg2, arg3);  // ← ここで適切なパラメータが渡されるか確認したい

  ...
  // 後処理
}
```

こういったテストケースを解決するための道具として、**テストダブル**というものがあります。
このテストダブルを用いると、前述の例で言うところの`funcA()`に対し`funcB()`のフリをする影武者を忍び込ませることができ、その影武者を操ることで`funcB()`がどのように呼ばれたかを確認できるようになります。

Mochaのお供として使用されるテストダブルとして、**[Sinon.js](http://sinonjs.org/)**と呼ばれるものがあります。
Mochaを含めた構成では度々登場しますので、覚えておくと良いでしょう。
※Jasmineには標準でテストダブルの機能が含まれています。

#### ※テストフレームワークのお供その２（アサーションライブラリ）
テストフレームワークの役割として**テスト結果を見やすく出力する**というものがあります。
この、テスト結果の出力に特化した機能を持つものとして、**アサーションライブラリ**と呼ばれるものがあります。
前述のMochaではこの**アサーションライブラリ**を用いることで、テスト結果をより見やすく拡張できます。
有名なアサーションライブラリとしては、[**power-assert**（power-assert.js）](https://github.com/power-assert-js/power-assert)や**[Chai](http://chaijs.com/)**が挙げられます。
特にMochaを使用する場合power-assertも併せて使用する構成が多いため、覚えておくと良いでしょう。

---

#### ここまでのおさらい
* テスト自動化をするためにはテストコードを書かねばならない
* テストコードを効率良く書くため、テストフレームワークが存在する
* JavaScriptでは**Mocha**や**Jasmine**というテストフレームワークがある
* Mochaを使う場合は、**Sinon.js**や**power-assert**なども併せて使うと良い

おぉ、なんだかテストコードが書けそうな品揃えができましたね！
あとは各々について詳しく調べてテストコードを書くだけです！
めでたしめでたし。

・・・

はい、勘の良い方なら気付いたと思います。
確かにテストコードは書けそうな雰囲気ですが・・・

**そのテストコードはどこで実行するの？**

そうなんです。
前段の例で挙げたRSpecやPHPUnitなどはそれぞれrubyやphpで動くものなので、ターミナル上でコマンドを叩けば実行できます。
コマンドで実行できるということはCIツールなどでも簡単に呼び出せます。
では、JavaScriptの場合はどうなるのでしょうか。

WebアプリケーションにおけるJavaScriptの動作環境は・・・そう、**ユーザのブラウザ上**ですね。
ブラウザ上で実行するとした場合、**どうやってテストコードを読み込ませるのか？**
いわゆるブラウザに搭載されている開発ツールを使うとしても、**手動で準備する必要がある**のでは？
そうなってしまうと、もはや**テスト自動化と言えない**のでは？

何やら怪しい空気が立ち込めてきました。
しかし、冒頭に出てきた謎の呪文の中にこれらの解決方法があります！
というわけで、ここからある意味本編とも言える、**JavaScriptテストコード実行基盤**の話へと移ります。

---

### 2. JavaScript実行環境
これも有名なため改めて書くのも気が引けるのですが・・・
テスト自動化にあたり、ブラウザ以外の場所でJavaScriptを動かす必要が出てきました。
JavaScriptの実行基盤には、言わずと知れた**[NodeJS](https://nodejs.org/ja/)**があります。
改めて説明する必要もありませんね。サーバサイドJavaScriptの立役者です。
JavaScriptのテスト自動化をするにあたり、暗黙の了解のような形で**NodeJS**は組み込まれています。
（他にも理由もありますが、それは後述します）

### 3. トランスパイラ
NodeJSを利用してテストを実施するにあたり、大きな問題があります。
ブラウザ上で動かしていたコードのままでは、NodeJSでうまく動かせないことが多いのです。

分かりやすい例えを出すと、jQueryなどの外部ライブラリを利用しているケースが挙げられます。
通常ブラウザで動かすと想定した場合、外部ライブラリを読み込む場合はどのようにするでしょうか？
`<script src="lib/jquery-min.js"></script>`のように、HTML上でScriptタグを記載しますよね。
NodeJSでは**直接JavaScriptを実行する**ため、このようなタグを書いて外部ライブラリを読み込むことはできません。
では、どのようにして外部ライブラリを読み込めば良いのでしょうか。

実はJavaScriptでは、外部ライブラリ読み込み用に`import/export`という構文が用意されています。
NodeJSなどで外部ライブラリを読み込む場合はそちらを利用すれば解決できます。
ということで、NodeJSでテストする場合には、テスト対象のコードで`import/export`を利用するように書き換える必要があります。

しかし、ここでまた問題が発生してしまいます。
この`import/export`ですが、現段階ではどのブラウザでも実装されていないんです。
つまり

* ブラウザで動くコード　→　NodeJSで動かない
* NodeJSで動くコード　→　ブラウザで動かない

詰みました。

・・・
と思いきや、ちゃんとこれを解決する救世主がいるんです。
それが**トランスパイラ**です。

**トランスパイラ**とは、あるプログラムソースを、同等の振る舞いをする別のプログラムソースに変換するツールの総称です。
この**トランスパイラ**を使い、テスト実行後にNodeJSで動くコード　→　ブラウザで動くコードへと変換をかけるようにします。
JavaScriptの変換では**[Babel](https://babeljs.io/)**や**[webpack](https://webpack.github.io/)**、**[Browserify](http://browserify.org/)**が有名です。


#### ※ JavaScriptの書き方について
先ほど`import/export`がブラウザでは動かないという話を書きました。
皆さんも使いたい言語機能が環境によってサポートされてない、なんて事態に遭遇したことがあるかと思います。
よくある理由としては、実行環境で**インストールされているバージョンが古い**などですね。
※ うわ、このサーバ未だにPHPのバージョン4系使ってるのかよ・・・みたいな。

あれ、ちょっと待ってください。
**JavaScriptのバージョン**ってどうなってるんでしょうか？
そもそもJavaScriptは、どこの誰が(言語仕様を)作っているのでしょうか？

これについて紆余曲折あったようですが、現在は[Ecma International](http://www.ecma-international.org/default.htm)という団体が決めた仕様がデファクトスタンダードとして扱われています。
この団体によって**ECMAScript**という名前で標準化され、**ECMA 2015**（別名:ES6）や **ECMA 2016** といったバージョンがあります。
新しいバージョンのECMAScriptでは**クラスが使えたり（！）**するのですが、残念ながら**現状のブラウザではサポートされていません。**

・・・あれ、これってさっきのNodeJSのときと同じ状況ですね。

ということで、もうお分かりかと思いますが**トランスパイラ**を使います。
この**ECMA2015**などで書いたJavaScriptは、前述の**トランスパイラ**によってブラウザで動くソースへ変換できます。
そのため、**最新のイケてる仕様でJavaScriptを書きたい！**という場合にもトランスパイラは利用されます。


### 4. ブラウザ（と、タスクランナー）
前段でブラウザからNodeJSに切り替えたのに、なんでまたブラウザが？と思う方がいるかと思います。
でもよく考えてみてください、NodeJSはJavaScriptを直接実行するんです。
通常のブラウザではどうなっているでしょうか？
**ドキュメントとしてHTMLが読み込まれ、その1要素としてJavaScriptが実行されます**よね。
この**ドキュメント（DOM）**部分のテストがNodeJSではできないんです。（※一応モジュールを使えばできます）
フロントエンドのJavaScriptはテキストの色やボタンのenabledが変わるなど、DOMへ干渉することが多いです。
それらの振る舞いをテストするには、やはり実際のブラウザで動かすのが一番なわけです。

しかし当然のことながら、自分でブラウザを起動して・・・なんてナンセンスなことはしません。
コマンドから起動できて、自動でテスト実行してくれるようにします。
そこで利用するのが**タスクランナー**です。

有名なタスクランナーとして**[Karma](https://github.com/karma-runner/karma)**や**[gulp](http://gulpjs.com/)**が挙げられます。
この**Karma**や**gulp**を使うことで、ブラウザにテスト対象のコードを読み込ませ、自動でテストが実施できます。
ちなみにこのKarmaやgulpはNodeJSで実行されます。
（前述の、暗黙の了解としてNodeJSが組み込まれる理由はこれです）

また、テストで使用するだけなら、わざわざリッチな見た目のブラウザなんて必要ありませんよね。
そんな時に便利なものとして、**ヘッドレスブラウザ**という**画面が描画されない**ブラウザがあります。
有名なものとして、**[PhantomJS](https://github.com/ariya/phantomjs)**が挙げられます。
タスクランナーでは実行するブラウザを選択できますので、この**PhantomJS**を選ぶのも良さそうです。

## まとめ
#### テストフレームワーク
* Jasmine
* Mocha

### テストダブル
* SinonJS

### アサーションライブラリ
* power-assert
* Chai

#### JavaScript実行環境
* NodeJS

#### トランスパイラ
* Babel
* webpack
* Browserify

#### JavaScript言語仕様
* ECMA 2015（ES6）
* ECMA 2016

#### タスクランナー
* Karma
* gulp

#### ヘッドレスブラウザ
* PhantomJS

## 最後に
長々と書いてしまいましたが、如何でしたでしょうか。
あくまで最初の取っ掛かりとして書いたため、具体的な導入手順や利用方法については記載しませんでした。
実際の導入手順、構成などは参考になるリンクを紹介しますので、そちらを参照いただければと思います。
リンク先の記事について抵抗無く読めるようになっていれば幸いです。

※ 記事中の誤りなどがありましたら、コメントにてご指摘ください。

## 参考リンク
■ [フロントエンドにテストを導入](http://qiita.com/howdy39/items/cdd5b252096f5a2fa438)
実際のコードを含め、テストを導入するまでの流れがまとめてあります。

■ [Karma + PhantomJS + Mocha + power-assertでクライアントサイドJSのユニットテスト環境を構築してみた](https://liginc.co.jp/187209)
実際に構築されたテスト自動化環境を公開されています。
この記事を読んだ後であればタイトルの呪文が理解できるかと思いますが、いかがでしょうか？

■ [ゼロから始めるJavaScript生活](http://qiita.com/takahashim/items/7838334d1451fb0a9811)
テスト自動化に限らない、最近のJavaScript開発環境におけるツールを紹介されています。