フロントエンドのテストとNode.js

## はじめに

Node.js と連携することで、昔は大変だったフロントエンドのテストがとても便利になってます。
という内容です。


## はじめに QUnit があった

JS のテスト手段として、自分の観測範囲で最も古い（4, 5年前に知った）ものが [QUnit](https://qunitjs.com/) でした。

このテスティングフレームワークは、例えばこのように使用します：

```html
<html>
  <head>
    <link rel="stylesheet" href="//code.jquery.com/qunit/qunit-1.20.0.css">
    <script src="//code.jquery.com/qunit/qunit-1.20.0.js"></script>
    <script src="foo.js"></script>
    <script src="bar.js"></script>
    <script src="tests.js"></script>
  </head>
  <body>
    <div id="qunit"></div>
  </body>
</html>
```

- ライブラリである `qunit-x.x.x.js` を読み込み
- テスト対象のロジックである `foo.js` や `bar.js` を読み込み
- 最後にテストコードである `tests.js` を読み込み、実行する
- テスト結果は、当 HTML ページにこのように描画される

![image](https://qiita-image-store.s3.amazonaws.com/0/12017/e7444121-1c01-8688-3507-d045eb17a787.png)

非常にシンプルです。
今でも状況によっては悪くない手法だとは思っています。

- しかし、一部の疎結合なロジックしかテストできないことや、
- モジュール間の依存関係を手動で解決するのが手間だったことや、
- 1ページにものすごい量の実行結果が出て、当時は重かったことや、
- そもそも専用のHTMLページを開かないといけないことなど、

個人的には使用していましたが、仕事で導入するのは難しそうだと感じていました。

（ただ今思うと、それは QUnit のせいではなく、自分自信がテストの意味に懐疑的だったという問題だったと感じています）


## mocha

2, 3 年前です。

しばらく、JS の開発から離れていた、というか仕事すらしてなかった時期があったのですが、その後の社会復帰活動の一環として JS 開発環境の再整備を行いました。

その時に知ったのが [mocha](http://mochajs.org/) です。
ただこの時は、「QUnit の後継で API が便利になったもの」という程度の認識でした。


## mocha on Node.js

最初は QUnit 的に mocha を使用していたのですが、Node.js という環境？からコマンドラインでも実行できることを知りました。

```bash:mochaコマンドからのテストコード実行例
$mocha test/tests.js

  numberFormat
    ✓ should convert from 900 to "900"
    ✓ should convert from 9000 to "9,000"
    1) should convert from 9000.1 to "9,000.1"

  2 passing
  1 failing
```

この mocha コマンドによるテストは、以下の点で非常にUXが高かったので：

- サーバサイドの開発も同時平行で行う場合、黒い画面で実行できると良い
- 終了ステータス取得など、OS と連携できる
- 実行がとても速い
- PhantomJS などを介した際に時折発生する、不安定な挙動が無い

可能な限り、Node.js環境でも動かせるようにJSモジュールを書くようになりました。

```js:こんな感じのコード
if (typeof window === 'undefined') {
  var dependedModule = require('./dependedModule');
}
 
var foo = {};

foo.sayFoo = function() {
  console.log('Foo!');
};

if (typeof window === 'undefined') {
  module.exports = foo;
} else {
  window.foo = foo;
}
```

## Grunt, gulp.js

Node.js 製、自分が知る限り初代のタスクランナー [Grunt](http://gruntjs.com/) です。
いまでは、どちらかというと [gulp.js](http://gulpjs.com/) の方が使われていると思います。

これは、テストに限らず様々な問題を解決するタスクを提供してくれました。

- 例えば、ファイルの concat や minify
- 例えば、CoffeeScript からのトランスパイリング
- 例えば、ソースコードの watch

これらのタスクを組み合わせるのも可能で、一気通貫なビルド＆テストを随時実行するようなこともできるようになりました。

例えば：

- ソースコードの `*.coffee` を watch して、
- 変更された `.coffee` を `.js` に変換し、 
- mocha でテストを実行、
- 成功したら `public/` へコピーして
- `public/tests.html` から読み込ませたページをブラウザで表示かリロードしてブラウザテスト

こんなフローです。（内容としては良くないので、使わないで下さい）


## browserify

革命的な魔法の杖、それが [browserify](http://browserify.org/) です。

説明が難しいので詳しくは端折りますが、先に書いたこれが：

```js:ブラウザ/Node.js両対応コード
if (typeof window === 'undefined') {
  var dependedModule = require('./dependedModule');
}
 
var foo = {};

foo.sayFoo = function() {
  console.log('Foo!');
};

if (typeof window === 'undefined') {
  module.exports = foo;
} else {
  window.foo = foo;
}
```

以下のように 100% Node.js として書いても、**ブラウザで**動くようになりました。

```js
var dependedModule = require('./dependedModule');
 
var foo = {};

foo.sayFoo = function() {
  console.log('Foo!');
};

module.exports = foo;
```


## そして現在

今は、大半のJSコードは、Node.js として書いても、フロントエンドJSの開発ができるようになりました。

そして、Node.js のコードである限り、コマンドラインによる安定・高速・便利なテストを利用することができます。
つまり、Ruby や Python などでテストを書きながら開発するのと同じようにです。

ブラウザに近い部分、つまりは DOM が必要になる部分のテストも、これも端折りますが、React などの Virtual DOM を介したビューライブラリを使えば、ブラウザに近いギリギリの部分までを、コマンドラインで検証することが、現実的に可能です。

実際のブラウザを使ったテストであっても、[Testem](https://github.com/testem/testem) などを使えば、Node.js 用に書いた mocha コードをそのまま再利用することができ、またクロスブラウザで実行してくれます。
加えて、コマンドラインからその結果を取ることもでき、つまりは CircleCI などの CI 環境で実行することも可能になっています。

一部、例えばブラウザの機能を使う箇所などで、どうしても実環境に頼らないと行けない部分はありますが、それらを考慮して少なく見積もっても、6, 7割程度はテストで動作を確認しながら開発することが出来る世界になっています。


## ポエミーな締め

現在、自分は Ruby/Rails と Java のエンジニアさんが多く所属する会社で、フロントエンドエンジニアリングな仕事をしています。

その中で感じているのは、今回書かせていただいたテストの話のような、ある程度 JavaScript を触れている人の中では常識のようなことも、まだまだ「サーバーサイド」の人々には共有されていないということです。

Node.jsやフロントエンド界隈は流れが早いのもあり、優れた意見やエッジな意見しか価値がないのかなーと思われがちですが、実際はまだまだ浸透度が低いので、皆様が普通だと思っているノウハウもどんどん発信していくべきだと思います。

そして当然のようにテストが書かれ、
そして当然のように動作し、保守される。

フロントエンドの開発がそこまで到達したら、
それは、とても素敵な状況ではないでしょうか？