Guardとは
Guardとはファイルの変更を検知して、自動的にさまざまな処理を実行してくれるRubyのGemです。
これ単体で使うよりも、他のツールと連携し、自動的に処理を行うことにより開発効率を上げることができます。
メジャーどころとしては、次の3つだと思います。
- guard-livereload - Viewファイルの変更したときに自動的にブラウザをリロードする
- guard-rspec - specファイルを変更したときに自動的に
RSpec
を実行する - guard-rubocop - ファイルを修正したときに
RuboCop
を実行する
本記事では、Railsへguard-rspec
の導入方法を記載します。
RSpecを自動的に実行することで、ソースがいつ壊れたか簡単に検知できるようにします。
対象読者
- Railsの開発効率を上げたい方
確認バージョン
目次
1. Railsプロジェクトの作成 + RSpec
まず、Guardを実行させるベースとなるRailsのプロジェクトを作成します。
rails new guard_test cd guard_test
次に、RSpecをGemfile
に追加します。
# Gemfile group :development, :test do gem 'rspec-rails' end
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-rspec
をRailsプロジェクトに導入していきます。
まずは、Gemfile
にguard-rspec
とterminal-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 空文字を許容しないこと
さらに、テストが失敗するので、右上にデスクトップ通知が表示されます。
では、Productモデルを実装しましょう。
# app/models/product.rb class Product < ActiveRecord::Base validates :name, presence: true end
保存するとまた特定のspecファイルのみ実行され、実行結果がデスクトップに表示されます。
これで、自動的に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')