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

Rails Webook

自社のECを開発している会社で働いています。Rails情報やサービスを成長させる方法を書いていきます

Railsの開発効率を上げる - guard-rspec 自動でテスト(RSpec)を実行させる

開発を効率化する Rails gem

f:id:nipe880324:20141016010205p:plain:w320

Guardとは

Guardとはファイルの変更を検知して、自動的にさまざまな処理を実行してくれるRubyのGemです。
これ単体で使うよりも、他のツールと連携し、自動的に処理を行うことにより開発効率を上げることができます。

メジャーどころとしては、次の3つだと思います。

本記事では、Railsguard-rspecの導入方法を記載します。
RSpecを自動的に実行することで、ソースがいつ壊れたか簡単に検知できるようにします。

対象読者

  • Railsの開発効率を上げたい方

確認バージョン

目次

  1. Railsプロジェクトの作成 + RSpec
  2. guard-rspecの導入
  3. guard-rspecのオプション


1. Railsプロジェクトの作成 + RSpec

まず、Guardを実行させるベースとなるRailsのプロジェクトを作成します。

rails new guard_test
cd guard_test

次に、RSpecGemfileに追加します。

# Gemfile

group :development, :test do
  gem 'rspec-rails'
end

Bundlerでrspec-railsを入れます。

bundle install

そして、RSpecの設定ファイルを追加します。

rails g rspec:install
      create  .rspec
      create  spec
      create  spec/spec_helper.rb
      create  spec/rails_helper.rb

Warningがたくさん表示されてしまうので、rspecコマンドのオプションを記載するファイルの.rspecを次のように修正します。

# .rspec

--color
--require spec_helper


次に、簡単なProductのScaffoldを作成します。

rails g scaffold Product name:string price:integer discontinued:boolean

マイグレートします。

rake db:migrate

ではRSpecを実行してみましょう。

rspec

...
Finished in 0.27292 seconds (files took 2.15 seconds to load)
30 examples, 0 failures, 17 pending

30件のテストの内、失敗は0件、17件はペンディング。つまり、13件が成功しました。


2. guard-rspecの導入

では、準備が整い舞いましたので、guard-rspecRailsプロジェクトに導入していきます。

まずは、Gemfileguard-rspecterminal-notifier-guardを追加します。

# Gemfile

group :development do
  gem 'guard-rspec', require: false # guardでrspecを動かす
  gem 'terminal-notifier'
  gem 'terminal-notifier-guard'  # デスクトップ通知を行う
end

Bundlerを実行して、インストールします。

bundle install

Guardfile(Guardの設定を記載するファイル)に「RSpec用の設定」を追加します。

guard init rspec


作成されたGuardfileの中身を確認してみます。
guard :rspecのブロック内に監視対象と実行するSpecファイルが記載されています。
具体的には、watchメソッドで「監視するファイル」を正規表現で記載し、ブロック内で監視するファイルが変更されたときに「実行するファイル」を指定します。

以下の設定で、適切な単位でのテスト実行がされ、失敗時にはデスクトップに通知されるため修正はしません。
guard-rspecのオプションについては、「guard-rspecのオプション」を参照して下さい。

# Guardfile

guard :rspec do
  watch(%r{^spec/.+_spec\.rb$})
  watch(%r{^lib/(.+)\.rb$})     { |m| "spec/lib/#{m[1]}_spec.rb" }
  watch('spec/spec_helper.rb')  { "spec" }

  # Rails example
  watch(%r{^app/(.+)\.rb$})                           { |m| "spec/#{m[1]}_spec.rb" }
  watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$})          { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
  watch(%r{^app/controllers/(.+)_(controller)\.rb$})  { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
  watch(%r{^spec/support/(.+)\.rb$})                  { "spec" }
  watch('config/routes.rb')                           { "spec/routing" }
  watch('app/controllers/application_controller.rb')  { "spec/controllers" }

  # Capybara features specs
  watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$})     { |m| "spec/features/#{m[1]}_spec.rb" }

  # Turnip features and steps
  watch(%r{^spec/acceptance/(.+)\.feature$})
  watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$})   { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
end||<


では、<code>Guard</code>を実行します。
>||
bundle exec guard

Railsプロジェクトの配置パスに日本語が入っていると、「E, [2014-10-16T00:43:38.055423 #28197] ERROR -- : run() in thread failed: inspected result must be ASCII only or use the same encoding with default external:/Users/nipe0324/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/listen-2.7.9/lib/listen/listener.rb:329:in `inspect'」のようなエラーが発生するのでご注意ください。


Pendingであるproduct_spec.rbにテストを追加してみましょう。

require 'rails_helper'

RSpec.describe Product, :type => :model do
  describe "#name" do
    it "空文字を許容しないこと" do
      product = Product.new(name: "", price: 100, discontinued: false)
      expect(product).to be_invalid
    end
  end
end

spceファイルを保存すると、guardが変更を検知し、特定のspecファイルを自動的に実行します。

15:50:03 - INFO - Running: ./spec/models/product_spec.rb:5
Run options: include {:locations=>{"./spec/models/product_spec.rb"=>[5]}}
F

Failures:

  1) Product#name 空文字を許容しないこと
     Failure/Error: expect(product).to be_invalid
       expected `#<Product id: nil, name: "", price: 100, discontinued: false, created_at: nil, updated_at: nil>.invalid?` to return true, got false
     # ./spec/models/product_spec.rb:7:in `block (3 levels) in <top (required)>'

Finished in 0.00922 seconds (files took 2.5 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/models/product_spec.rb:5 # Product#name 空文字を許容しないこと

さらに、テストが失敗するので、右上にデスクトップ通知が表示されます。
f:id:nipe880324:20141019155242p:plain:w320

では、Productモデルを実装しましょう。

# app/models/product.rb

class Product < ActiveRecord::Base
  validates :name, presence: true
end

保存するとまた特定のspecファイルのみ実行され、実行結果がデスクトップに表示されます。
f:id:nipe880324:20141019155503p:plain:w320


これで、自動的にRSpecを実行する環境を構築できました。
また、guardを実行しているコンソール上でEnterを押すと、全てのSpecファイルを実行することが可能です。


3. guard-rspecのオプション

次のようにguard-rspecにオプションを指定することが可能です。

# Guardfile

# bundle execをつけて、rspecコマンドを実行する
guard :rspec, cmd: 'bundle exec rspec' do
  # ...
end

指定できるオプションは次のようになっています。

cmd: 'zeus rspec'      # rspecコマンドの前に特定のコマンドをつけてrspecを実行できる(デフォルト値:'rspec')
cmd_additional_args: '-f progress' # rspecのオプションを指定できる
spec_paths: ['spec']   # specファイルのカスタム配列を指定できる
failed_mode: :focus    # specが失敗した時の挙動
                       # 使用可能な値:
                       #  :focus - focus on the first 10 failed specs, rerun till they pass
                       #  :keep - keep failed specs until they pass (add them to new ones)
                       #  :none (default) - just report
all_after_pass: true   # specファイルが変更されたら全てのspecを実行する(デフォルト値:false)
all_on_start: true     # guard起動時に全てのspecを実行する(デフォルト値:false)
launchy: nil           # rspecの結果ファイルの出力パスを指定する。 例: ./tmp/spec_results.html(デフォルト値:nil)
notification: false    # specが実行されたあとに通知を表示するか指定する(デフォルト値: true)
run_all: { cmd: 'custom rspec command', message: 'custom message' } # Custom options to use when running all specs
title: 'My project'    # 通知のタイトルを指定できる(デフォルト値:'RSpec results')

参考文献

guard/guard-rspec · GitHub


以上です。分からない箇所や間違いなどありましたらコメントください。