A Nice Rails Testing Setup
So I was recently asked about how I test. I realized that I have a very nice setup (for me anyway) and I though I would share a bit of it with you. It keeps my testing fast and flowing. Testing can some times be such a chore, and these configurations and steps help to reduce that feeling.
Note: I have a gem
I do use a gem that configures 99% of this for me. It’s internal to my business. It has a lot of things people always look for and a few modifications that are specific to my business. For example these testing configs, and the code that auto enters tickets into my tracker when an exception is thrown. So if you see any missing parts it’s probably in that gem.
Second Note: I am not going to teach testing
That’s outside the scope of this document. If you don’t know how to write tests then this article is not for you. I would like to remind all developers out there not to go crazy with the testing. Keep it small, simple, and atomic.
group :development, :test do gem 'sqlite3', :require => 'sqlite3' gem 'autotest-growl' gem 'autotest' gem 'autotest-standalone', :require => 'autotest' gem 'autotest-rails-pure' gem 'ansi' gem 'autotest-fsevent' gem 'fabrication' gem 'rspec-rails' gem 'spork-rails' gem "shoulda-matchers" end gem 'simplecov', :require => false, :group => :test
First, yes I load testing stuff in development. It helps track down problems with the testing framework. You may want to change that. It’s a taste thing. sqlite3 is chosen over mysql or others because it helps promote a database agnostic design. Spork makes things faster but has some gotchas, and yes I use RSPEC.
The .rspec file
--colour --drb -f h -o coverage/docs.html -f p -t ~slow
Colour is british, just pretend it says color. –drb tells rspec to use spork. -f h tells it to use HTML -o coverage/docs.html is output the HTML to a file. -f p tells it to use the default dots output.
I output to HTML so that I can look at the results in a browser, and then use the default to help autotest (and the command line) get back reasonable STDOUT for parsing.
I also tag slow tests and don’t run them after they pass once. This means tests need to be manually run every once-in-a-while, but thats far better then running 30 60 second tests every time you save a file. Normally these are API consumption tests, and end up getting mocked or stubbed in other tests.
I like it better the guard, so thats what I use. Guard should work here as well. Here is the ~/.autotest file:
require 'autotest/growl' require 'autotest/fsevent' require 'ansi' Autotest.add_hook :red do |at| system("afplay ~/.bin/red.mp3") end Autotest.add_hook :green do |at| system("afplay ~/.bin/green.mp3") end
Growl does what you think. Makes it grow test status, much better then staring at terminal. Fsevent helps autotest hook in for “file changed” events, instead of polling, it’s much faster. The hooks play sounds that help me notice failures in another way.
I always test using apache and passenger. Too many times in the past I have been bitten by issues that are highlighted with passenger that mongrel, webrick, etc. just ignore. This means that my dev setup is closer to production.
<VirtualHost *> ServerName test-project.coteyr.net DocumentRoot /Users/coteyr/Projects/test-project/public/ <Directory /Users/coteyr/Projects/test-project/public/ > AllowOverride All Allow from all Options -MultiViews </Directory> Alias /coverage /Users/coteyr/Projects/test-project/coverage <Directory /Users/coteyr/Projects/test-project/coverage > AllowOverride All Allow from all </Directory> </VirtualHost>
The important part there is the alias to something outside the public directory.
The Test Helper
I am not going to post the entire file. It’s too big, but the important part is:
Spork.prefork do unless ENV['DRB'] require 'simplecov' SimpleCov.start 'rails' do add_filter "/specs" end end ########More stuff Here Spork.each_run do # This code will be run each time you run your specs. if ENV['DRB'] require 'simplecov' SimpleCov.start 'rails' do add_filter "/specs/" end end end
All together now
Here is a screen shot of a project in the very early stages.
Ok, so like most developers, I do the standard write a failing test, fix code, test passes cycle. The only difference is that I don’t look at the terminal. It’s there in the background running. So in Terminal:
spork & autotest
Then in a browser (I use chrome for developing). Go to:
This allows you to watch the browser for pass/fail and use it to make sure the site “looks” right. Having the coverage report open also gives you a quick glance at if your cheating without noticing. Growl (and the sounds) tells you the status of the test run, and the browser will tell you the details of the error in a nice pretty highlighted way.
I should also mention that I use LiveReload to auto refresh pages as they get updated by me or by autotest/rspec.
I only run into 3 gotchas. First, spork is a bit different then running rake from the command line. Most notably it doesn’t reload the entire environment every single run. This means that if you add gems or do other things where you would normally restart the web server, you also have to restart spork.
Second, your not running rake and autotest doesn’t auto migrate the database. So if you make database level changes you need to run rake once. I don’t feel that this is a bad thing because it backs up any issues that you may have missed because your running spork (like removing a gem).
Last, autotest with fsevent can only register files that exist. So if you add a file (like model or request test) autotest won’t know about it. Switching to the autotest terminal and pressing ctrl+c (there by running the entire test suite) fixes the issue.
Coteyr.net Programming LLC. is about one thing. Getting your project done the way you like it. Using Agile development and management techniques, we are able to get even the most complex projects done in a short time frame and on a modest budget.
Feel free to contact me via any of the methods below. My normal hours are 10am to 10pm Eastern Standard Time. In case of emergency I am available 24/7.
Phone: (813) 421-4338