Sep 4, 2014

Focusing your Guard

Guard is great for getting rapid feedback when you'd doing tdd/bdd.  By default, the guard plugins for spec and cucumber watch and run a lot.  Recently I've found myself wanting to just have guard only run the tests which I am focusing on.

After setting up guard-rspec and guard-cucumber I updated my Guardfile to:


def watch_cucumber
  watch(%r{^features/.+\.feature$})
  watch(%r{^features/support/.+$})          { 'features' }
  watch(%r{^features/step_definitions/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'features' }
end

def watch_rspec
  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" }
  watch('spec/rails_helper.rb')                       { "spec" }

  # 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


group :tests do
  guard :cucumber, cmd: 'spring cucumber' do
    watch_cucumber
  end

  guard :rspec, cmd: 'spring rspec' do
    watch_rspec
  end
end


# Run guard -g focus
# It will watch all the reguslar rspec and cucumber watches files
# but will only run:
# cucumber tests tagged with @focus
# rspec tests with :focus => true
group :focus do
  logger level: :warn
  logger template: ':message'

  guard :rspec, cmd: 'spring rspec --tag focus' do
    watch_rspec
  end

  guard 'cucumber', command_prefix: 'spring', bundler: false, cli: '--profile focus' do
    watch_cucumber
  end
end

This creates 2 groups ('focus' and 'tests'). The focus group only runs the tests that I have tagged as a focus. ('@focus' in cucumber and 'focus: true' in spec). The focus:cucumber guard file definition tells cucumber to use the profile 'focus'. Alternatively I could have used the focus_on option but a profile feels cleaner. In order to use a profile you need to set it up in cucumber.yml like so:


focus: --format progress --strict --tags @focus

Then, when I want guard to automatically run only the tests I've tagged with focus, I run:


guard -g focus

I find this really helps me get into the flow when writing code. If I'm doing something that could affect a to of areas, I can still run guard -g tests to fall back on the default guard behaviour.