Rails / RSpec テスト書いたことない メンドクサイ(n´Д`)という時のチートシート

Shunsuke Sawada

テストを書かないようになってしまっていた。
大きなアプリケーションではないし一人で作ってるし… まぁいいか的な。

ただ、コードの量が増えていくにつれやっぱりちょっと辛い。
書き方すっかり忘れたので、RSpec再入門。

  
test-it

環境
Vagrant + CentOS (centos64box)
Ruby 2.2.1
Rails 4.1.1
  
最終的なGemfile(テスト関係だけ)
テスト用のDBの設定をお忘れなく。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
gem 'pg'

group :development, :test do
  gem 'rspec-rails'
  gem 'factory_girl_rails'
  gem 'guard-rspec'
  gem 'spring-commands-rspec'
end

group :test do
  gem 'faker'
  gem 'capybara'
  gem 'database_cleaner'
  gem 'poltergeist'
  gem 'shoulda-matchers'
end

準備

1
$ rails g rspec:install

.rspecファイルに --format documentation を追加すると表示結果がキレイになる。

デフォルトのテストフレームワークをRspecにするのと、
rails g した時に作られるファイルを制御。

config/application.rb

1
2
3
4
5
6
7
8
9
10
config.generators do |g|
  g.test_framework :rspec,
    fixtures: true,
    view_specs: false,
    helper_specs: false,
    routing_specs: false,
    controller_specs: true,
    request_specs: false
  g.fixture_replacement :factory_girl, dir: "spec/factories"
end

FactoryGirl

thoughtbot/factory_girl_rails

使う際に便利な設定をしておく。
  
FactoryGirl.create(:contact) の代わりに
create(:contact) と書けるようになる。

spec/rails_helper.rb

ruby
1
2
3
RSpec.configuredo|config|
  config.include FactoryGirl::Syntax::Methods
end

  
コールバックを使うと手間が省ける。
Fakerも便利。

spec/factories/mobel.rb

ruby
1
2
3
4
5
6
7
8
9
10
11
12
13
14
FactoryGirl.define do
  factory :model do
    firstname { Faker::Name.first_name }
    lastname { Faker::Name.last_name }
    email { Faker::Internet.email }

    after(:build) do |mobel|
      [:type1, :type2, :type2].each do |type|
        model.other_models << FactoryGirl.build(:other_model, type: type)
      end 
    end

  end
end

マクロ

何回も使う処理はマクロにしてしまおう。

spec/supports フォルダを作って
login_macros.rb に定義されたマクロを読み込む設定。

spec/rails_helper.rb

ruby
1
2
3
4
RSpec.configure do |config|
  Dir[Rails.root.join("spec/supports/**/*.rb")].each{ |f| require f }
  config.include LoginMacros
end

カスタムマッチャー

supports/matchers/require_login.rb
定義すると expect(response).to require_login とか書ける。

1ファイルにつき1マッチャなので注意。

ruby
1
2
3
4
5
RSpec::Matchers.define:require_login do |expected|
  match do |actual|
    expect(actual).to .....
  end
end

フィーチャースペック

features/....rb にファイルを追加。
Javascriptが必要な場合は js: true を付ける。

ruby
1
2
3
4
5
feature "Something" do
  scenario "do something", js: true do
   .....
  end
end

フィーチャースペックにはJavascriptはほぼ必須だろうから、
PhantomJSをインストールする。
本ではSeleniumだけど、結構手間がかかるので PhantomJS がいい。
これならサクっといきます。
Seleniumについては前に書いてみたのもある。
  
参考
PhantomJSをCentOS6にインストール - Qiita
初心者大歓迎!RSpec 3でドラッグアンドドロップ機能をテストする方法(スクリーンキャスト付き) - Qiita
  
database_cleaner も入れておく。

database strategies てなに?
transactiontruncation 何が違うの?というのは、下記が参考になった。
ruby - Difference between truncation, transaction and deletion database strategies - Stack Overflow
  
Gemfile

1
2
gem 'poltergeist'
gem "database_cleaner"

  
spec/rails_helper.rb

ruby
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
require 'capybara/poltergeist'
require 'database_cleaner'

RSpec.configure do |config|

  config.use_transactional_fixtures = false

  # Js driver
  Capybara.javascript_driver = :poltergeist

  # Cleaner
  config.before(:suite) do
    DatabaseCleaner.strategy = :truncation
    DatabaseCleaner.clean_with(:truncation)
  end
  config.before(:each) do
    DatabaseCleaner.start
  end
  config.after(:each) do
    DatabaseCleaner.clean
  end
end

ちなみに、
find_button('Close').click などの待ち時間は下記のように設定できる。

ruby
1
Capybara.default_wait_time = 15

Shoulda

便利なマッチャ thoughtbot/shoulda-matchers
これがあれば自分で書くコードが少なくなる。

Gemfile

1
gem 'shoulda-matchers'

こんな感じでね。

ruby
1
2
subject { Contact.new }
it { should validate_presence_of(:firstname).with_message("名前は必須です") }

Guard

ファイルを変更した時に自動的にテストを走らせる。

Gemfile

1
2
3
group :development, :test do
  gem 'guard-rspec'
end

  
このコマンドでGuardfileが作成される。
必要に応じてオプションを変更/追加する。

1
$ bundle exec guard init rspec

  
Guardfile

ruby
1
2
3
guard :rspec, cmd: "spring rspec --color --format documentation", all_on_start: false do
  ...
end

notification: false
all_on_start: false
all_on_pass: false
など。

Vagrant のローカル開発環境でやっていて、
Macの Sublime Text でファイルを編集しているんだけども、
ファイル変更時に Guard が走らなかった。

Guard起動時にオプションを渡すことで解決。

1
$ bundle exec guard -p -l 10

参考
Vagrant + Guard 環境でファイル変更が検知されない - Qiita

Spring

テストの実行が速くなるよ。

Gemfile

1
2
3
group :development, :test do
  gem 'spring-commands-rspec'
end
1
2
$ bundle exec spring binstub rspec
$ bin/spring stop

  
Guardfileにspringを指定

ruby
1
2
3
guard :rspec, cmd: "spring rspec --color --format documentation", all_on_start: false do
  ...
end

そのほか便利な機能

タグ機能

1
2
3
it "processes the deal", focus: true do
  # example
end

rspec --tag focusfocus: true のテストだけ実行。

あらかじめ設定しておくことも出来る。
spec/rails_helper.rb

ruby
1
2
3
4
RSpec.configure do |config|
  config.filter_run focus: true
  config.filter_run_excluding slow: true
end

  
メール送信をテスト
bmabey/email-specというGemが便利。

ファイルのアップロードのテスト
spec/factories に保存しているファイルを使う。

時間をテスト
travisjeffery/timecop が便利。

外部サービスをテスト*
vcr/vcr が便利。

テストはFeature Specから書くといいそうな。
Outside-inだと。

今回読んだ本

Everyday Rails - RSpecによるRailsテスト入門

前にお世話になった Ruby on Rails チュートリアル:実例を使って Rails を学ぼう
似たような感覚で読み進められる。RSpec + Rails にフォーカスしてるのでありがたい。

2日間で一気に読んだ + 実験したけど、
明日から自分のサービスのテストが書き始められそう。
まだメンドクサイ(n´Д`)という気持ちはあるけどね…。

TDDには程遠いけど、書いていこう。
  

223
Shunsuke Sawada

おすすめの記事

シドニーは都会だから、メモリ増設だってできちゃう。
justhostがハッキングされてる件
オーストラリアに来て1年が経ったのでブログを更新