generator-backbone (Yeoman) で生成した RequireJS を使うアプリを mocha でテストする

[Yeoman](http://yeoman.io/) の [generator-backbone](https://github.com/yeoman/generator-backbone) を使って [Backbone.js](http://backbonejs.org/) と [RequireJS](http://requirejs.org/) を使うアプリを作ろうとしたら、テストコードの追加方法や書き方がわからなかった。

いろいろ試行錯誤して何とかテストコードが書ける＆実行できるようになったので、以下にまとめる。

なお、これが正しいやり方なのかどうかはわからない。もっとスマートな方法があるのかもしれない。

## 事象

generator-backbone で RequireJS を使うようにすると、テストコードが生成されない。

調べると GitHub に [Issue](https://github.com/yeoman/generator-backbone/issues/252) が切られていた。 generator-backbone が依存している [generator-backbone-mocha](https://github.com/revathskumar/generator-backbone-mocha/) が RequireJS を使ったテストの生成に対応していないらしい。

## 解決方法

[こちらのエントリー](http://projectpoppycock.com/the-best-way-to-test-requirejs-code-with-mocha-phantomjs-and-grunt/ "The Best Way to Test RequireJS code with Mocha, PhantomJS, and Grunt.") を参考に、 [grunt-shell](https://github.com/sindresorhus/grunt-shell) と [mocha-phantomjs](https://github.com/metaskills/mocha-phantomjs) を使ってテストを作成した。

## 手順

### コード生成

Yeoman を使ってコードを生成する。

```console
% mkdir bbapp && cd $_
% yo backbone --requirejs
% npm install && bower install
% cd test
% bower install
% cd ..
```

### 必要なモジュールのインストール

テストに必要なモジュールのインストールを行う。

```console
% npm install grunt-shell --save-dev
% npm install mocha-phantomjs --save-dev
```

### mocah でテストするための設定

Gruntfile.js を以下のように修正

```diff:Gruntfile.js
@@ -136,11 +136,12 @@ module.exports = function (grunt) {
                 'test/spec/{,*/}*.js'
             ]
         },
-        mocha: {
-            all: {
+        shell: {
+            'mocha-phantomjs': {
+                command: 'node_modules/.bin/mocha-phantomjs -R dot http://localhost:<%= connect.test.options.port %>/index.html',
                 options: {
-                    run: true,
-                    urls: ['http://localhost:<%= connect.test.options.port %>/index.html']
+                    stdout: true,
+                    stderr: true
                 }
             }
         },
@@ -332,7 +333,7 @@ module.exports = function (grunt) {
                 'jst',
                 'compass',
                 'connect:test',
-                'mocha',
+                'shell:mocha-phantomjs',
             ];

         if(!isConnected) {
```

`test/index.html` も以下のように修正した。

```html:test/index.html
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>Mocha Spec Runner</title>
    <link rel="stylesheet" href="bower_components/mocha/mocha.css">
</head>
<body>
    <div id="mocha"></div>
    <script src="bower_components/mocha/mocha.js"></script>
    <script>mocha.setup('bdd')</script>

    <script data-main="main.js" src="bower_components/requirejs/require.js"></script>
</body>
</html>
```

さらに `test/main.js` を作成し以下のように記述した。

```js:test/main.js
/*global require, mocha */
'use strict';

require.config({
    paths: {
        jquery: 'bower_components/jquery/dist/jquery',
        backbone: 'bower_components/backbone/backbone',
        underscore: 'bower_components/lodash/dist/lodash',
        bootstrap: 'bower_components/sass-bootstrap/dist/js/bootstrap',
        chai: 'bower_components/chai/chai'
    }
});

require([
    'spec/models/train'
], function () {
    if (window.mochaPhantomJS) {
        mochaPhantomJS.run();
    } else {
        mocha.run();
    }
});
```

### Backbone.js のモデルを生成とテストの作成

テストを書く準備ができたので、モデルを生成してテストを書く。

まずはモデルの生成。以下のコマンドで `app/scripts/models/item.js` が生成される。

```console
% yo backbone:model item
```

先の Issue にあるように、テストコードは自動生成してくれないので自分で作成する。

`test/spec/` 以下に、ジェネレーターが生成したコードと同じ階層になるようにモデルに対するテストコードを作成した。

```js:test/spec/models/item.js
/* global define, describe, it */

define([
    'chai',
    'backbone',
    'scripts/models/item'
], function (chai, Backbone, ItemModel) {
    'use strict';

    chai.should();

    describe('ItemModel', function () {
        it('should be an instance of Backbone.Model', function () {
            var item = new ItemModel();
            item.should.be.an.instanceof(Backbone.Model);
        });
    });
});

```

追加したテストコードのパスを `test/main.js` に追加する。

```diff:test/main.js
@@ -11,7 +11,9 @@ require.config({
     }
 });

-require([], function () {
+require([
+    'spec/models/train'
+], function () {
     if (window.mochaPhantomJS) {
         mochaPhantomJS.run();
     } else {
```

### テストの実行

```console
% grunt test
(... snip ...)

Running "shell:mocha-phantomjs" (shell) task
Error loading resource http://localhost:35729/livereload.js?snipver=1 (1). Details: Connection refused

  ․

  1 passing (4ms)


Done, without errors.

(... snip ...)
```

Livereload のエラーが出るけど、テスト自体は成功する。

ちなみに、 mocha-phantomjs を使わずジェネレーターが生成した Gruntfile.js のままだと、テストが認識されず実行されない。

この辺のしくみはちゃんと理解できていないのだけど、まずはテストが書ける＆実行できるようになった。これでようやくコードを書き始められる。。。