Ruby | Guard gem を利用してファイルの変更を検出し、任意のタスクを自動実行する
概要
Guard gem を利用してファイルの変更を検出し、任意のタスクを自動実行する。
今回はファイル変更時に RSpec を実行してみます。
インストール
- guard の RSpec Plugin も一緒にインストールします
$ gem install guard --no-ri --no-doc
$ gem install guard-rspec --no-ri --no-doc
設定ファイル(Guardfile)の生成
RSpec対応のひな形を生成します。
$ guard init 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
設定ファイルの編集
下記のように編集します。
Guardfile
guard :rspec do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" }
end
guardを起動します
$ guard start
プログラムのひな型を生成します
自作 gem の rspec_piccolo を利用します。
rspec_piccoloはプロダクトコードとテストコードのひな形を生成する CLI ツールです。
詳しくは
https://github.com/tbpgr/rspec_piccolo
を参照。
piccolo e Hoge hoge hoge -p
これで、以下のファイルが生成されます。
lib/hoge.rb
# encoding: utf-8
class Hoge
def hoge
# TODO: implement your code
end
end
spec/hoge_spec.rb
# encoding: utf-8
require "spec_helper"
require "hoge"
describe Hoge do
context :hoge do
cases = [
{
case_no: 1,
case_title: "case_title",
expected: "expected",
},
]
cases.each do |c|
it "|case_no=#{c[:case_no]}|case_title=#{c[:case_title]}" do
begin
case_before c
# -- given --
hoge = Hoge.new
# -- when --
# TODO: implement execute code
# actual = hoge.hoge
# -- then --
# TODO: implement assertion code
# expect(actual).to eq(c[:expected])
ensure
case_after c
end
end
def case_before(c)
# implement each case before
end
def case_after(c)
# implement each case after
end
end
end
end
自動生成されたファイルを検知して、guardが実行されます
[1] guard(main)>
22:45:17 - INFO - Running: spec/hoge_spec.rb
Run options: include {:focus=>true}
All examples were filtered out; ignoring {:focus=>true}
.
Finished in 0 seconds
1 example, 0 failures
テストを実装後、プロダクトコードを保存したguardの実行結果を確認します
spec/hoge_spec.rb
# encoding: utf-8
require "spec_helper"
require "hoge"
describe Hoge do
context :hoge do
cases = [
{
case_no: 1,
case_title: "valid case",
expected: "hoge",
},
]
cases.each do |c|
it "|case_no=#{c[:case_no]}|case_title=#{c[:case_title]}" do
begin
case_before c
# -- given --
hoge = Hoge.new
# -- when --
actual = hoge.hoge
# -- then --
expect(actual).to eq(c[:expected])
ensure
case_after c
end
end
def case_before(c)
# implement each case before
end
def case_after(c)
# implement each case after
end
end
end
end
[1] guard(main)>
22:47:05 - INFO - Running: spec/hoge_spec.rb
Run options: include {:focus=>true}
All examples were filtered out; ignoring {:focus=>true}
F
Failures:
1) Hoge hoge |case_no=1|case_title=valid case
Failure/Error: expect(actual).to eq(c[:expected])
expected: "hoge"
got: "# TODO: implement your code"
(compared using ==)
# ./spec/hoge_spec.rb:28:in `block (4 levels) in <top (required)>'
Finished in 0.001 seconds
1 example, 1 failure
Failed examples:
rspec ./spec/hoge_spec.rb:17 # Hoge hoge |case_no=1|case_title=valid case
プロダクトコードを実行して、テストをパスするようにしてファイルを保存します。
テストが成功することを確認できました
lib/hoge.rb
# encoding: utf-8
class Hoge
def hoge
'hoge'
end
end
[1] guard(main)>
22:48:46 - INFO - Running: ./spec/hoge_spec.rb:17
Run options: include {:locations=>{"./spec/hoge_spec.rb"=>[17]}}
.
Finished in 0.001 seconds
1 example, 0 failures
Run options: include {:locations=>{"./spec/hoge_spec.rb"=>[17]}}
.
Finished in 0.001 seconds
1 example, 0 failures
参照
https://github.com/guard/guard
http://rubygems.org/gems/guard
Guard まとめ記事