Guard is Your Friend

Get instant test feedback using Guard::RSpec and other Guard plugins.

Guard of Buckingham Palace by MaryG90 is licensed under [CC BY-SA 3.0] (http://creativecommons.org/licenses/by-sa/3.0)

Often when I’m pairing with other developers, there is a moment where I notice that their process and habits are significantly different than mine. In many cases, this introduces me to something new that I never knew about, and that I’m excited to weave into my own process. But then there are some things that really break me out of my flow, and can distract me from the task at hand. Manually running tests, especially targeted tests, is one of those things.

It might not seem like a big deal, but switching between windows and typing the command to run the test I want to focus on is a lot of work. It takes precious seconds away that could be better used thinking about solving the problem, rather than trying to remember which directory my test is in, or which line I need to run now that the test location shifted since the last run. These are distractions, and they can be easily removed with a bit of automation. Enter Guard - a command line tool to automatically handle events on file system modifications.

While Guard is running, it can be configured to watch certain files on your file system, and then react in a certain way when the files it’s watching change. I can tell it to watch my project, and run tests whenever I save a file. Even better, I can tell it to run specific tests depending on which files changed. Let’s try it out. Add Guard and Guard::RSpec to your Gemfile:

group :development, :test do
  gem "rspec-rails"
  gem "guard"
  gem "guard-rspec"
end

Once you install the bundle, generate the Guardfile:

bundle exec guard init rspec

This will generate the default Guard configuration script, which should look something like this:

guard :rspec, cmd: "bundle exec rspec" do
  require "guard/rspec/dsl"
  dsl = Guard::RSpec::Dsl.new(self)

  # Feel free to open issues for suggestions and improvements

  # RSpec files
  rspec = dsl.rspec
  watch(rspec.spec_helper) { rspec.spec_dir }
  watch(rspec.spec_support) { rspec.spec_dir }
  watch(rspec.spec_files)

  # Ruby files
  ruby = dsl.ruby
  dsl.watch_spec_files_for(ruby.lib_files)

  # Rails files
  rails = dsl.rails(view_extensions: %w(erb haml slim))
  dsl.watch_spec_files_for(rails.app_files)
  dsl.watch_spec_files_for(rails.views)

  watch(rails.controllers) do |m|
    [
      rspec.spec.call("routing/#{m[1]}_routing"),
      rspec.spec.call("controllers/#{m[1]}_controller"),
      rspec.spec.call("acceptance/#{m[1]}")
    ]
  end

  # Rails config changes
  watch(rails.spec_helper)     { rspec.spec_dir }
  watch(rails.routes)          { "#{rspec.spec_dir}/routing" }
  watch(rails.app_controller)  { "#{rspec.spec_dir}/controllers" }

  # Capybara features specs
  watch(rails.view_dirs)     { |m| rspec.spec.call("features/#{m[1]}") }
  watch(rails.layouts)       { |m| rspec.spec.call("features/#{m[1]}") }

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

This uses a fancy DSL for matching common rails patterns, so here’s a simplified example of what it’s doing:

guard :rspec, cmd: 'bundle exec rspec' do
  watch('spec/spec_helper.rb')                        { "spec" }
  watch('config/routes.rb')                           { "spec/routing" }
  watch('app/controllers/application_controller.rb')  { "spec/controllers" }
  watch(%r{^spec/.+_spec\.rb$})
  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{^lib/(.+)\.rb$})                           { |m| "spec/lib/#{m[1]}_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"] }
end

The watch method takes a string or regular expression to match paths that Guard should watch. It will yield the match data into the provided block, in which we return the path for the file we should run with RSpec using the command provided for the :cmd option. For example:

guard :rspec, cmd: 'bundle exec rspec' do
  watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
end

When any file under the app directory is saved, say, app/interactors/commerce/create_stripe_charge.rb, Guard will in turn run the command bundle exec rspec spec/interactors/commerce/create_stripe_charge_spec.rb. The generated Guardfile contains watchers for a lot of commonly related files. For instance, view or layout changes will run related feature specs. Controller changes will run routing, controller, and acceptance tests. You can customize these mappings to your heart’s content.

We can try this out with the default Guardfile. In the terminal, run bundle exec guard, and you’ll see it start a Pry console watching your code. Open up a model file and save it. Your tests for that model will start automatically. Saving a test file will run the tests in the saved file.

So what?

So maybe this doesn’t seem like a big deal, but I want to highlight some major benefits.

Focus

When you are developing a feature or fixing a bug, you want to stay focused on the task at hand. In many cases, this means running RSpec with a specific file and a specific line number, i.e. rspec spec/models/user_spec.rb:45. But that exclusive fixation can mean that while you’re writing code to make that one test pass, you’re introducing failures into other tests. Since you’re not running those tests, you’re not seeing that your changes are breaking the build until later. Guard helps solve the negatives of focusing too hard. But at the same time it also keeps us from losing focus by trying to remember the file and line number of the method you’re trying to test.

When running Guard, the RSpec’s inclusion filters are useful if I only want to run a targeted subset of tests (within a single file or across multiple files). Just make sure you clear focus tags before you commit to source control.

Fast, Continuous, Inclusive Feedback

With Guard running, I can write a test, save it, and by the time I’m switching to the terminal, my tests will usually be finishing up, delivering results immediately. I read why it fails, and I jump back to fix it, without ever having to switch contexts from Ruby to bash. I’m also seeing immediately if my changes will break other tests, so I don’t end up having to backtrack on a solution that I mistakenly thought was working. If I customize my watchers, I can even make sure, for example, that my serializer tests run when I change my model, and that my API is still functioning as expected. You can build critical connections into your development process to ensure quality before it even gets to continuous integration.

Keep Running Failing Tests

There’s an option I recommend you consider using:

guard :rspec, cmd: 'bundle exec rspec', failed_mode: :keep do
end

When you use <pre>failed_mode: :keep</pre>, any time a test fails, wherever in the test suite it is, Guard will include that test in future runs until it passes again. This makes for a really helpful workflow:

  1. Build a feature, letting Guard run related tests until everything is green.
  2. Run the whole test suite (in the Guard console, press ENTER).
  3. Find tests that failed unexpectedly because of my changes.
  4. Fix my the code until those are green again.
  5. Repeat as necessary.

This way, I’m not having to keep track of or hunt down failing tests on my own. I can focus on the work until everything is green and shiny.

Guarding Everything

Guard isn’t just limited to RSpec. There are a number of official and unofficial plugins. Here are just a few to get you started:

  • Guard::Rails will boot and watch your server, and restart it whenever something critical changes, like initializers or dependencies.
  • Guard::Yield can be used to run any code during Guard’s lifecycle events.
  • Guard::Rubocop will check code style whenever you’re writing code.
  • Guard::Brakeman will watch for security issues as you develop.

The possibilities are endless, and it’s not just for Ruby or Rails. Check out the list, or get creative and write your own Guards.

Whenever I start development on a feature, the first thing I do is boot up Guard in a terminal tab and run the whole suite, so I know I’m starting green. From then on, I can develop with confidence knowing Guard has my back.

Photo of Joshua Kovach

Josh grew up in Michigan and graduated from Grand Valley State University with a BS in Computer Science, with minors in Mathematics, Business, and Engineering. His skills include web and mobile development, and he enjoys developing APIs and Android apps. He is a mentor on HackHands, pairing with programmers working through coding issues.

A long-time puzzle enthusiast, Josh formerly wrote the weekly crossword puzzle for his college newspaper, the Grand Valley Lanthorn, and enjoys the challenge of solving the occasional gridless crossword. He lives in Wyoming, Michigan with wife Erin, daughter Mara, two cats, and bearded dragon.

Comments:


Post a Comment

(optional)
(optional — will be included as a link.)
  1. Thank you Joshua! This really cleared things up for me and got my guard working for rails and ruby testing. Best regards from the Netherlands!

    April 02, 2017 at 11:47 AM