読者です 読者をやめる 読者になる 読者になる

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