はじめに
フロントエンドなエコシステムだと、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でテストが動くという仕組みが出来ます。