Getting Started with Guard and RSpec

As I build out an application I want to ensure it’s behaving as I intend it. RSpec is a great framework for testing Ruby code, and is the tool I use most for my testing. But tests are pretty useless if you don’t run them, and rather than manually run tests when I change things, I use Guard and RSpec together. Here’s the simplest possible example for setting it up.

"Detroit Publishing Co. - A Yeoman of the Guard (N.B. actually a Yeoman Warder), full restoration" by Adam Cuerden, Detroit Publishing Company - http://www.loc.gov/pictures/item/2002696943/. Licensed under Public domain via Wikimedia Commons - http://commons.wikimedia.org/wiki/File:Detroit_Publishing_Co._-_A_Yeoman_of_the_Guard_(N.B._actually_a_Yeoman_Warder),_full_restoration.jpg#mediaviewer/File:Detroit_Publishing_Co._-_A_Yeoman_of_the_Guard_(N.B._actually_a_Yeoman_Warder),_full_restoration.jpg

Guard and RSpec : rather fancy

Guard is a command line tool that responds to filesystem change events. You can use it to do all sorts of stuff using one of the many plugins that have been built for it. In our simple example we are going to use it to trigger rspec tests when we change code in our app.

Getting Started

For this we are going to grab an example from the Spex repository where I keep all my code examples. Clone the branch from github:


git clone -b rspec_guard --single-branch https://github.com/eyefodder/spex.git

Confirming the tests run

When you have the code cloned, jump into the ops directory and execute vagrant up to bring up the virtual machine that will run our example. As instructed, run vagrant rsync-auto; this will ensure that if you make changes to the code, they will get synced in the virtual machine (which is where Guard will be running). Now, open a new Terminal window, vagrant ssh into the machine and type the following:

cd /app
rspec

By doing this, we should see our tests run and get the following output:

vagrant@spex:~$ cd /app
vagrant@spex:/app$ rspec
Run options: include {:focus=>true}

All examples were filtered out; ignoring {:focus=>true}
....

Finished in 0.29975 seconds (files took 1.65 seconds to load)
4 examples, 0 failures

Top 4 slowest examples (0.2972 seconds, 99.1% of total time):
  Static Pages root page has the expected title
    0.27993 seconds ./spec/integration/static_pages_spec.rb:9
  ApplicationHelper #some_method_to_test returns 'result'
    0.00696 seconds ./spec/helpers.lication_helper_spec.rb:4
  Static Pages root page has a link to my blog
    0.00691 seconds ./spec/integration/static_pages_spec.rb:13
  Static Routing root path routes to the static#home path
    0.0034 seconds ./spec/routing/static_routing_spec.rb:5

Top 3 slowest example groups:
  Static Pages
    0.14342 seconds average (0.28684 seconds / 2 examples) ./spec/integration/static_pages_spec.rb:2
  ApplicationHelper
    0.00696 seconds average (0.00696 seconds / 1 example) ./spec/helpers/application_helper_spec.rb:2
  Static Routing
    0.0034 seconds average (0.0034 seconds / 1 example) ./spec/routing/static_routing_spec.rb:2

Randomized with seed 13496

So what happens if we break something? Go ahead and change the code in app/helpers/application_helper.rb:

  def some_method_to_test
    'resultd'
  end

Now when we run rspec again we see that four tests have run and one failed as expected. But really, given we just changed one file, did we have to run all the tests? And did we have to manually re-run the tests? The answers to these is no; Guard and Rspec together are pretty awesome…

Guard and Rspec together

To get things up and running, type in the following:

bundle exec guard -p

You should see something like this:

vagrant@spex:/app$ bundle exec guard -p
21:18:32 - INFO - Guard is using NotifySend to send notifications.
21:18:32 - INFO - Guard is using TerminalTitle to send notifications.
21:18:32 - INFO - Guard::RSpec is running
21:18:32 - INFO - Guard is now watching at '/app'
[1] guard(main)> 

If you hit the Enter key, you’ll see all your tests run again. This is pretty awesome right? Now you can save yourself having to type rspec. But wait, that’s not all. Go ahead and change your application_helper.rb file back the way it was. Now you should see something like this:

21:22:13 - INFO - Running: spec/helpers/application_helper_spec.rb
Run options: include {:focus=>true}

All examples were filtered out; ignoring {:focus=>true}
.

Finished in 0.0103 seconds (files took 1.99 seconds to load)
1 example, 0 failures

Top 1 slowest examples (0.00878 seconds, 85.2% of total time):
  ApplicationHelper #some_method_to_test returns 'result'
    0.00878 seconds ./spec/helpers.lication_helper_spec.rb:4

Randomized with seed 15725


[1] guard(main)> 

Pretty sweet huh? Basically, anytime a file changes Guard figures out what test or tests we need to run. This helps to keep my test suite running lean and fast; aiding rather than slowing down development. Let’s take a look at what we have added in our example to make things work.

How this was setup

First off we added two gems to the Gemfile:

group :development do
  # rspec_guard
  gem 'guard-rspec', require: false
end

group :development, :test do
  # rspec_guard
  gem 'rspec-rails', '~> 3.0.0'
end

Next, we have written some specs which you will find in the spec folder. I’m not going to go into detail on the tests I’ve written, but they should be pretty legible and cover just enough to demonstrate what we need to about Guard. Check out spec/helpers/application_helper.rb, spec/integration/static_pages_spec.rb, and spec/routing/static_routing_spec.rb. The spec/rails_helper.rb and spec/spec_helper.rb files are the ones generated by the rspec-rails gem when we run rails generate rspec:install.

The Guardfile

The real magic of getting Guard and RSpec working together comes from the Guardfile which is used to tell Guard what to watch for, and then what to do with it. I’ve modified the file generated by guard init rspec to demonstrate some common usage patterns based on the way I test. Watch statements follow simple regular expressions to determine when to fire. As you build up your app, you will want to keep this file updated as you may need to introduce new patterns. Let’s take a look through some examples in the file:

Run any spec that changes

Any file that is in the spec folder and ends with ‘_spec.rb’ will get run if it changes:

  # run a spec file if it changes
  watch(%r{^spec/.+_spec\.rb$})

Re-run the spec if the helper files change

spec_helper.rb and rails_helper.rb are files that RSpec uses to setup your specs. So if they change for any reason, it would affect all the tests.

  # re-run if spec helper or rails helper changes
  watch('spec/spec_helper.rb')  { "spec" }
  watch('spec/rails_helper.rb')  { "spec" }

Run the matching spec if something changes in the app folder

As a good catch all, if a file changes in the app folder we want to run the corresponding spec:

  # run a file matching same path with _spec at the end
  # eg /app/models/foo.rb will run /spec/models/foo_spec.rb
  watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }

Re-run the suite if support files change

I put RSpec shared examples and custom matchers into the support folder, so if anything in here changes I’ll need to re-run the full suite:

  watch(%r{^spec/support/(.+)\.rb$})  { "spec" }

Integration tests for views and controllers

I have found it simpler to test my views and controllers by creating integration tests that mimic user behaviour (although the tests you see aren’t great examples of that, we’ll be building out better ones in a future example on using Capybara). For this to work, I have a naming structure such that specs related to the foo controller (or views) live in spec/integration/foo_pages_spec.rb. If a controller is pluralized, that is handled well too, so that app/controllers/things_controller.rb will trigger spec/integration/thing_pages_spec.rb:

  watch(/^app\/controllers\/(.+)_controller.rb/) do |m|
    ["spec/integration/#{m[1].singularize}_pages_spec.rb",
     "spec/routing/#{m[1]}_routing_spec.rb"]
  end

And if a view file changes, we run the corresponding integration test using this:

  # if something within a view folder changes, run that spec
  # eg app/views/static/anyfile runs /spec/integration/static_pages_spec.rb
  watch(%r{^app/views/(.+)/}) do |m|
      "spec/integration/#{m[1].singularize}_pages_spec.rb"
  end

If any of the overall layouts change, it could affect any of our integration tests, so we should re-run them all:

  watch(%r{^app/views/layouts/(.*)}) { 'spec/integration' }

Abstract classes

Lastly, you will inevitably end up having abstract classes in your app. If these change you probably want to run all the specs for classes that inherit from it. I only included an example for the application_controller.rb changing but you can extrapolate from here:

  watch('app/controllers/application_controller.rb')  { "spec/integration" }

A word on Guard with polling

The more astute amongst you will notice that Guard is started up with a -p option. This option forces Guard to poll for changes rather than listen for filesystem change notifications. The reason is that with our setup using rsync to synchronize changes in the virtual machine causes Guard to not always respond to changes. Basically an earlier issue with Guard-Rspec was solved by having rspec only run when it sees a file as modified rather than added. However, with rsync it seems that the listen gem sometimes think the file has been added rather than modified. I’m trying to figure out a resolution for this, but in the meantime, polling it is!

But wait, there’s more!

This super-simple example should help you get up and running with Guard and RSpec. With the setup here you would find that every time the specs run the Rails application has to load up. This makes every test run take at least a few seconds. In earlier versions of Rails I used to use Spork to pre-load the framework, but now Rails comes with Spring which is much easier to setup. In my next post I’ll walk you through it; until then happy testing!

This entry was posted in Agile Software Development, Engineering, Quality Software, Rails, Ruby, Software Craftsmanship, Test Driven Development. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.