Search code examples
rspecguard

How do you create a Guard configuration watch that will only run the test file that was changed?


There wasn't much information how to do this online but in my Guardfile I am setting up the watch for the various directories/files that I want Guard to be watching for changes on. How do you specify a watch for changed spec/test files that have been updated and only run that specific file when that happens? Thanks!

guard 'rspec' do
  watch(%r|^spec/.+_spec\.rb$|)               { "spec" }
end

Currently the above runs all of the spec/tests when a file is changed under the spec directory. But I only want that changed test file's tests to be run.

Current spec directory tree:

spec
 -models
  -user_spec.rb
  -classroom_spec.rb
 -controllers
  -classroom_spec.rb
 -lib
  -support_spec.rb

Thanks!


Solution

  • The Guard RSpec plugin has a demo Guardfile that you can create with

    $ bundle exec guard init rspec
    

    and the content is as follows:

    # A sample Guardfile
    # More info at https://github.com/guard/guard#readme
    
    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)$})                 { |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)$})          { |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
    

    I recommend to stick with this, because it contains many best practices for file/folder naming and triggering associated specs.

    To answer your question, if you omit the block of the watch expression, Guard uses the exact file that had a change:

    guard 'rspec' do
      watch(%r|^spec/.+_spec\.rb$|)
    end
    

    That would trigger the RSpec plugin for each modified file in the spec folder that ends with _spec.rb.

    The block to the watch expression is there to transform the filename to something else and the parameter to the black is the match of the regexp, so you can add some special logic to it. Short example demonstrating this:

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

    A file change in lib/file.rb will trigger the plugin and the block converts that file name to spec/lib/file_spec.rb, because m[1] contains the match group from the RegExp, which is file.