yo_waka's blog

418 I'm a teapot

Rails4で書いたアプリをMiniTest::Specでテストする

Rails4からはActiveSupport::TestCaseがTest::UnitからMiniTest::Unit::TestCaseのサブクラスに変わっている。
MiniTestはSpecなDSLをサポートしているので、RSpecを入れずともBDDスタイルでテストが書けるようになる。
ということで、いろいろtest_helper.rbをゴニョってたらminitest-rails-specというズバリなGemを見つけたので(><)これを使う。

minitest-spec-railsでやってくれることは、ざっくりいうと、RailsActiveSupport::TestCaseにMinitest::Spec::DSLをextendして、ControllerとかHelperクラスをテスト対象クラスに追加して、beforeとかafterとかのDSLを使えるようにしてくれるだけというシンプルな作りになっている。

使えるDSLはチートシートにまとめてくれている人がいるけど、ソースコメントにも使い方が書いてあるので数も多くないしRDocか直接のぞいて見てみるのがよさげ。

environments/test.rbなどに以下を書くと、context(describeのエイリアス)やshould(itのエイリアス)も使えるようになる。

config.minitest_spec_rails.mini_shoulda = true

Gemfile

gem 'rails', '4.0.0.beta1'
group :test do
  gem 'minitest-spec-rails'
  gem 'factory_girl_rails'
end

Factoryはこんな感じで定義しておく

FactoryGirl.define :tester1, class: User do
  name "tester1"
  password "tester1tester1"
  email "tester1@example.com"
end

まずは普通にモデルをテスト

test/models/user_test.rb

require 'test_helper'

describe User do
  context '#attributes' do
    it '#name is required' do
      tester1 = Factory.create :tester1
      
      proc = Proc.new do
        tester1.update(name: "")
      end
      proc.must_raise ActiveRecord::RecordInvalid
    end
  end
end
$ bundle exec rake test:models
Run options: --seed 30682

# Running tests:

.

Finished tests in 1.190526s, 6.1302 tests/s, 3.2388 assertions/s.

1 tests, 1 assertions, 0 failures, 0 errors, 0 skips

よさそう。 続いてAPIコントローラのテスト。 登録ユーザーの一覧をJSONで返すAPIが「Api::UsersController」クラスのindexメソッドに定義されているのでテストする。

test/controllers/api/users_controller_test.rb

require 'test_helper'

# describeメソッドにテストするコントローラクラスをセットすることで、テストメソッド内で"get :index"など書くと、
# ActionController::TestRequestリクエストを作って、
# コントローラのアクションを実行して、
# 結果をActionController::TestResponseで判定できる
describe Api::UsersController do
  before {
    20.times do |idx|
      User.create(
        name: "user#{idx}",
        password: "user#{idx}password",
        email: "user#{idx}@example.com"
      )
    end
  }

  it "#GET /api/users - とりあえず全件返す" do
    get :index
    res = JSON.parse(response.body)
    res["users"].size.must_equal 20
  end
end

ログインしないと叩けないAPIはテスト用にログインメソッド作ってやると書きやすい気がする。

test_helper.rbに追加

class ActionController::TestCase
  include ApplicationHelper

  def start_session_test(user_id)
    @controller.reset_session
    @controller.session[:login_id] = user_id
  end
end

これをリクエストを発行する前に実行すればいい

require 'test_helper'

describe Api::UsersController do
  before {
    @tester1 = Factory.create :tester1
  }

  it "#GET /api/my - ログインユーザー情報を返す" do
    start_session_test @tester1.id # tester1でログイン

    get :my
    res = JSON.parse(response.body)
    res["user"]["name"].must_equal "tester1"
  end
end

RailsアプリのJavaScriptをkonachaを使ってCLI上でテストする

前回のでアプリケーションのテストがMiniTest::Specで実行できるようになったので、今度はJavaScriptユニットテストをコマンドラインから実行できるようにする。

慣れてるMochaを使ってテストが書けるkonachaと、konachaが使用するCapybaraのPhantomJSドライバであるpoltergeistを使う。

Gemfile

group :test do
  gem 'konacha'
  gem 'poltergeist'
end

konachaの設定はinitializerで。
ドライバをpoltergeistにして、テスト対象ディレクトリを"test/javascripts"に変えた(デフォルトは"spec/javascripts")。

config/initializers/konacha.rb

if defined?(Konacha)
  Konacha.configure do |config|
    require 'capybara/poltergeist'
    config.driver = :poltergeist
    config.spec_dir = "test/javascripts"
  end
end

mochaの設定はテストディレクトリ直下にspec_helper.jsを置いてその中に書くらしい。

test/javascripts/spec_helper.js

// set the Mocha test interface
// see http://visionmedia.github.com/mocha/#interfaces
mocha.ui('bdd');

// ignore the following globals during leak detection
mocha.globals(['util']);

// or, ignore all leaks
mocha.ignoreLeaks();

// set slow test timeout in ms
mocha.timeout(5);

テストディレクトリ以下にある、"hoge_test.js"あるいは"hoge_spec.js"というファイルがテストファイルとなる。 もちろんSprokectsを使ってRailsのasset pipelineを使ってテストしたいJSファイルを読み込むことができる。
precompile済みならapplication.jsを指定するくらいか。

//= require spec_helper
//= require application

あとはchaiアサーションを使ってテストを書いてkonacha:runすればphantomjsでテストが実行される!

RAILS_ENV=test bundle exec rake konacha:run