Rails4

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

More than 1 year has passed since last update.

はじめに

フロントエンドなエコシステムだと、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

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

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

テストランナーとは、様々なブラウザでテストを実行し、その結果をまとめてレポートするためのツールを指します。
Jasmine × Karma × Gulp でつくるユニットテスト環境 入門 – AngularJS + TypeScript #3

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

インストール&細かい設定

インストール自体はGitHubのinstallation通りにやれば問題ないかと。

spec/javascripts/spec_helper.jsの修正

spec/javascripts/spec_helper.jsを開くと

//= require application

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

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

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

ちょっとハマった所

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

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

みたいにしてるので、

//= require application
//= require angular
//= require some_module1
//= require some_module2

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

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

./bin/rails generate teaspoon:install

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

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を使って開発しており

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を作成して以下のように

//= 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に以下内容を書きました

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実行する

bundle exec teaspoon

を実行すると

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

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

CircleCI上でteaspoonを動作させる

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

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

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