Rails TDD environment setup with Guard and Foreman

Emmanuel DelgadoMar 01, 2012

This article shows my current TDD environment setup, it's all based on many of the links listed at the end of this article.

This setup simply automates your TDD process. Let's start by adding the required gems to your Gemfile:

group :test do
  # rspec goodies
  gem 'rspec-rails'

  # DRb server for testing frameworks
  gem 'spork'

 # command line tool to easily handle events on file system modifications
  gem 'guard'
  gem 'guard-bundler'
  gem 'guard-rspec'
  gem 'guard-spork'

 # run some required services using foreman start, more on this at the end of the article
  gem "foreman"
end

Why should I use spork?

Have you ever been crying about your testing suite being so damn slow that you can not event follow a TDD flow, and of course, blaming perhaps bundler or the big amount of gems required by the rails environment?. By using spork you'll load your rails environment in memory only once (this will depend on guard configuration) and then the environment will be forked, which is faster than having to read from the file system over and over again.

Why should I use guard?

Because you will need to use different guard implementations to observe changes on the file system and trigger bundler, rspec and spork reloads.

OK, now lets install your bundle:

(master) 1349m $ bundle

Create a Guardfile

The next step is to create a Guardfile in order to set up some basic guard configurations, create a Guardfile in the root of your project:

guard 'bundler' do
  watch('Gemfile')
end

guard 'spork', wait: 60, cucumber: false, rspec: true, test_unit: false do
  watch('config/application.rb')
  watch('config/environment.rb')
  watch(%r{^config/environments/.+\.rb$})
  watch(%r{^config/initializers/.+\.rb$})
  watch('Gemfile')
  watch('spec/spec_helper.rb')
end

guard 'rspec', version: 2, cli: '--drb --format progress --color' do
  watch(%r{^spec/.+_spec\.rb$})
  watch(%r{^spec/factories/(.+).rb$})
  watch(%r{^lib/(.+)\.rb$})     { |m| "spec/lib/#{m[1]}_spec.rb" }
  watch('spec/spec_helper.rb')  { "spec" }

  # Rails example
  watch(%r{^spec/.+_spec\.rb$})
  watch(%r{^app/(.+)\.rb$})                           { |m| "spec/#{m[1]}_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"] }
  watch(%r{^spec/support/(.+)\.rb$})                  { "spec" }
  watch('spec/spec_helper.rb')                        { "spec" }
  watch('app/controllers/application_controller.rb')  { "spec/controllers" }
end

This file simply instructs guard about what to do when file changes occur, whenever a file matching the following regular expressions changes then either run rspec on the file, reload the spork environment, or run bundle install:

  • watch(%r{^spec/.+_spec.rb$}) changes run rspec on that file
  • watch('config/application.rb') changes reload spork environment
  • watch('Gemfile') changes run bundle install

Also take into account the following line:

guard 'rspec', version: 2, cli: '--drb --format progress --color' do

It tells rspec ro run within a drb.

Setting up rspec

Now, let's setup rspec, let's start with the basic structure of your spec/spec_helper.rb file:

require 'spork'

Spork.prefork do
  # Loading more in this block will cause your tests to run faster. However,
  # if you change any configuration or code from libraries loaded here, you'll
  # need to restart spork for it take effect.

  # This file is copied to spec/ when you run 'rails generate rspec:install'
  ENV["RAILS_ENV"] ||= 'test'
  require File.expand_path("../../config/environment", __FILE__)
  require 'rspec/rails'
end

Spork.each_run do
  load "#{Rails.root}/config/routes.rb"
  Dir["#{Rails.root}/app/**/*.rb"].each {|f| load f}
  Dir["#{Rails.root}/lib/**/*.rb"].each {|f| load f}

  # This code will be run each time you run your specs.
  RSpec.configure do |config|
    config.mock_with :rspec

    config.use_transactional_fixtures = true
  end
end

Within the #prefork block you need to load the rails app, and do global setups, usually you can fill it with your whole old spec helper file.

In the #each_run reload everything that needs to be changing more dynamically. And here you'll put the main trick, load the routes, app and lib folders files every time rspec runs.

Let's test it:

 (master) 1388m $ bundle exec guard
Guard could not detect any of the supported notification libraries.
Guard is now watching at '/Users/emmanueldelgado/Projects/test'
Bundle already up-to-date
Starting Spork for RSpec
Using RSpec
Preloading Rails environment
Loading Spork.prefork block...

One step quick start with Foreman

Though we are done with the setup, there's one remaining final step that I like to use in order to provide a simple switch to start my development process, foreman.

Why do I need it? It happens to me that some times I need to run some solr servers, one for test and other for development environment, also in order to start my development day I need to execute many commands in order to start different services, example:

bundle exec rails s
bundle exec rake sunspot:solr:run
bundle exec rake sunspot:solr:run RAILS_ENV=test
bundle exec guard

We can encapsulate all this commands into simple one command foreman start. Let's create a file called Procfile with the following contents:

web:       bundle exec rails s
solr_dev:  bundle exec rake sunspot:solr:run
solr_test: bundle exec rake sunspot:solr:run RAILS_ENV=test
guard:     bundle exec guard

And that's it, start your day with a bundle exec foreman start:

(master) 1405m $ bundle exec foreman start
18:37:05 web.1        | started with pid 26479
18:37:05 solr_dev.1   | started with pid 26480
18:37:05 solr_test.1  | started with pid 26481
18:37:05 guard.1      | started with pid 26482
18:37:13 guard.1      | Guard could not detect any of the supported notification libraries.
18:37:13 guard.1      | Guard is now watching at '/Users/emmanueldelgado/Projects/crowdint/test'
Bundle already up-to-date
Starting Spork for RSpec
18:37:23 guard.1      | Using RSpec
18:37:24 guard.1      | Preloading Rails environment
18:37:46 solr_dev.1   | 2012-02-24 18:37:46.002::INFO:  Logging to STDERR via org.mortbay.log.StdErrLog
18:37:46 solr_dev.1   | 2012-02-24 18:37:46.336::INFO:  jetty-6.1.3
18:37:46 solr_dev.1   | 2012-02-24 18:37:46.599::INFO:  Extract jar:file:/Users/emmanueldelgado/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/sunspot_solr-1.3.0/solr/webapps/solr.war!/ to /var/folders/5g/18_2yg8s4ts7m_c0fr163qkm0000gn/T/Jetty_0_0_0_0_8982_solr.war__solr__-2c5peu/webapp
18:37:50 web.1        | [2012-02-24 18:37:50] INFO  WEBrick 1.3.1
18:37:50 web.1        | [2012-02-24 18:37:50] INFO  ruby 1.9.3 (2011-10-30) [x86_64-darwin11.0.0]
18:37:50 web.1        | [2012-02-24 18:37:50] INFO  WEBrick::HTTPServer#start: pid=26479 port=3000
18:37:50 solr_dev.1   | 2012-02-24 18:37:50.868::INFO:  Started SocketConnector @ 0.0.0.0:8982
18:37:52 solr_test.1  | 2012-02-24 18:37:52.687::INFO:  Logging to STDERR via org.mortbay.log.StdErrLog
18:37:52 solr_test.1  | 2012-02-24 18:37:52.969::INFO:  jetty-6.1.3
18:37:53 solr_test.1  | 2012-02-24 18:37:53.071::INFO:  Extract jar:file:/Users/emmanueldelgado/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/sunspot_solr-1.3.0/solr/webapps/solr.war!/ to /var/folders/5g/18_2yg8s4ts7m_c0fr163qkm0000gn/T/Jetty_0_0_0_0_8981_solr.war__solr__-opvtuv/webapp
18:37:55 solr_test.1  | 2012-02-24 18:37:55.556::INFO:  Started SocketConnector @ 0.0.0.0:8981
18:37:56 guard.1      | Loading Spork.prefork block...
18:38:01 guard.1      | Spork is ready and listening on 8989!
Spork server for RSpec successfully started
18:38:02 guard.1      | Guard::RSpec is running, with RSpec 2!
Running all specs     | 
18:38:07 guard.1      | Running tests with args ["--color", "--failure-exit-code", "2", "--format", "progress", "--format", "Guard::RSpec::Formatter::NotificationRSpec", "--out", "/dev/null", "--require", "/Users/emmanueldelgado/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/guard-rspec-0.6.0/lib/guard/rspec/formatters/notification_rspec.rb", "spec"]...
18:38:25 guard.1      | ...........................................................................................
18:38:25 guard.1      | 
18:38:25 guard.1      | Finished in 14.91 seconds
18:38:25 guard.1      | 91 examples, 0 failures

Conclusion

This setup has been working great for me, there are many other guards that you can setup in your application, and can help you do things like:

Look at the guard user on github to see all the many implementations they have, or perhaps even create your own guard.

References

Thanks to the following articles and softr8 base research (non published), they saved my life, all credit goes to them:

blog comments powered byDisqus