Rails＋AngularJSな構成でteaspoon+Jasmine＋CircleCI構築した時のメモ

## はじめに

フロントエンドなエコシステムだと、AngularJS 使った開発だとテストランナーにKarmaとかを選択するかと思います。

（フロントエンドに詳しい人が他にいないという事情があって）Ruby/Railsなエコシステムの中でどうにかして、Rails＋AngularJSな構成下で、JavaScriptのテスト実行環境をうまいこと構築出来ないと模索する中で最近教えてもらったteaspoonを活用することで自分がイメージしたことが出来たのでその流れをまとめておこうと思います。

## teaspoonとは？

> Javascript test runner for Rails. Run tests in the browser or headless with PhantomJS, Selenium WebDriver or Capybara Webkit.
> [teaspoon](https://github.com/modeset/teaspoon)

ということでRailsのためのテストランナー。

テストランナーが上手に説明できないですがこちらの説明が自分としてはわかりやすかったので引用しておきます

> テストランナーとは、様々なブラウザでテストを実行し、その結果をまとめてレポートするためのツールを指します。
> [Jasmine × Karma × Gulp でつくるユニットテスト環境 入門 – AngularJS + TypeScript #3](http://tech.recruit-mp.co.jp/front-end/post-5299/)

ひとまず今回目指してるのは、AngularJS使って書かれてるJavaScriptのテストで、E2Eのテストについては触れません。

## インストール＆細かい設定

インストール自体は[GitHubのinstallation](https://github.com/modeset/teaspoon#installation)通りにやれば問題ないかと。

### spec/javascripts/spec_helper.jsの修正

spec/javascripts/spec_helper.jsを開くと

```javascript
//= require application
```

という記述があるかと思うのですが、こんな感じ↓でAngularJSの読み込みを追加しました。

なお、applicationの前にangular＆angular-mocksを読み込んでおかないと上手く動作しないかと思うので注意が必要です

```javascript
//= require angular
//= require angular-mocks
//= require angular-resource
//= require application
```

#### ちょっとハマった所

自分が開発してるAngularJSのapp.jsで

```javascript
angular
  .module('MyApp', [
    'ngResource',
    'someModule1',
    'someModule2'
  ])
```

みたいにしてるので、

```javascript
//= require application
//= require angular
//= require some_module1
//= require some_module2
```

という感じで、MyAppが利用してるモジュールも読み込まないとうまく動作しませんでした。

### （任意）出力結果の表示をドット（.）からドキュメント形式に変更


```sh
./bin/rails generate teaspoon:install
```

とした時に生成される設定ファイル（spec/teaspoon_env.rb）をちょっとだけ修正しました。

```ruby
Teaspoon.configure do |config|
  config.mount_at = "/teaspoon"
  config.root = nil
  config.asset_paths = ["spec/javascripts", "spec/javascripts/stylesheets"]
  config.fixture_paths = ["spec/javascripts/fixtures"]
  config.formatters = [:documentation] # ←デフォルトの出力形式がドット（.）なのが好みでないのでdocumentationを指定
  config.suite do |suite|
    suite.use_framework :jasmine, "2.3.4"
    suite.matcher = "{spec/javascripts,app/assets}/**/*_spec.{js,js.coffee,coffee}"
    suite.helper = "spec_helper"
    suite.boot_partial = "boot"
    suite.body_partial = "body"
  end
end
```

## teaspoonの動作確認

自分が作ってるプロジェクトで管理画面の中で利用するJavaScriptでAngularJSを使って開発しており



```sh
app/assets/javascripts
├── admin
│   ├── app.js
│   ├── application.js
│   ├── controllers
│   └── directives
├── application.js
├── app.js
```

のようなディレクトリ構成にしてます。controllers/item.jsに対してテストを書いて、それにパスする実装というのを想定してこんな感じで書きました。


### Jasmineで書いたspec

spec/javascripts/admin/controllers/item_spec.jsを作成して以下のように

```javascript
//= require admin/app
//= require admin/controllers/item
describe('ItemCtrl', function () {
  var ctrl,
      scope,
      item = '{"name": "テスト"}';
  // Initialize the controller and a mock scope
  beforeEach(module('MyApp'));
  beforeEach(inject(function ($controller, $rootScope) {
    scope = $rootScope.$new();
    ctrl = $controller('ItemCtrl', {
      $scope: scope
    });
  }));
  describe('init()', function () {
    it('itemNameのscopeが参照できる', function () {
      scope.init(item);
      expect(scope.itemName).toBe('テスト');
    });
  });
```

### 実装ファイル

app/assets/javascripts/admin/controllers/item.jsに以下内容を書きました


```javascript
angular.module('MyApp')
  .controller('ItemCtrl', function ($scope) {
    $scope.init = function(item){
      var _item = JSON.parse(item),
          item_name;
      $scope.itemData = _item;
      $scope.itemName = item_name;
    };
  });
```

### 開発環境上でteaspoon実行する


```sh
bundle exec teaspoon
```

を実行すると


```
ItemCtrl
  init()
    itemNameのscopeが参照できる
```

という表示がされました！

## CircleCI上でteaspoonを動作させる

CircleCIが予想以上に簡単に設定できることをつい最近しって使い始めてるのでteaspoonも動作するようにために設定ファイルをこんな感じ作りました。


```yaml
machine:
  timezone:
    Asia/Tokyo
test:
  pre:
    - bundle exec teaspoon
```

これで、手元の開発環境で作業→PR作る→CircleCIでテストが動くという仕組みが出来ます。