Now that testing is a normal part of my daily workflow, I think it's a great time to go over some of the testing tools that I use here at thoughtbot.
Typing too much makes testing no fun at all. I'm actually coming from the RSpec world, and thankfully it was pretty easy to switch over to Shoulda from it. Here's what I like best about it:
setupblocks makes testing ridiculously simple and DRY.
before_should: Basically, you can set up expectations with your favorite mocking framework in these blocks, which run before the
setupblock in the given
- Macros that just save tons of time. Simple ones like
should_redirect_toare actions that need to be tested all the time, and luckily there's a whole suite of them for both ActiveRecord and ActionController.
- Custom macros that can be easily dropped in to test the same actions in multiple places. I do miss RSpec's
it_should_behave_likethough, but this works decently.
The concept of mocking and stubbing in general is somewhat new to me, and it's definitely been difficult to learn how to use effectively. It's starting to become natural though, and I don't mind the speedup that testing in isolation gives. The majority of our projects use Mocha, which does the job just fine. I've also been looking into RR, which has a much cleaner syntax and some additional features, but usually my daily mocking is done with Mocha. The most important thing for me to remember is to avoid situations where test pass but the code is broken due to excessive stubbing of behavior. The sweet spot with this is to use integration testing to cover all the bases.
If you've ever dealt with the frustration that is Rails fixtures, then you know the breath of relief that factories can give your tests. Being able to generate new models with a simple
Factory(:post) call makes testing ridiculously easy. Some of the latest features I've been using is the
:parent option on
Factory.define, which allows you inherit attributes from another factory. Clearance has a great example of this in how it creates a
Factory(:email_confirmed_user) that descends from
I've learned about user stories in classes, but being able to write them in plain English and run them with Ruby has totally blown me away. Outside-in development is a great way to approach features that need to be built and tested from the ground up. Ben Mabey's presentation at MountainWest RubyConf has a great example of how this development method works if you haven't tried it out yet. The best part I find is that it can be read by clients or developers coming into the project, and it can be used outside of the Rails environment as well. Take some time and look into Cucumber if you haven't already, and start at their fantastic site.
Mocking out external HTTP calls is difficult. If you're working with any sort of API from another site and you want to ensure that your code is actually submitting to it, relying on FakeWeb is a good choice. The syntax and setup is really simple, and it will usually look something like this:
require 'fake_web' FakeWeb.allow_net_connect = false FakeWeb.register_uri(:post, "http://somesite.com", :string => 'response body')
This little snippet will cause FakeWeb to throw errors if any other HTTP calls are made outside of the ones registered. The final line will tell the library to return the given string when that URL is hit from the application with a POST request. Like most projects in the Ruby world, it just works.
Testing in your shell
Being a convert to vim from TextMate, running tests usually isn't as simple as doing Cmd+R. Luckily there's two tools that help out with this. One is autotest, which I'll run if the test suite for the app I'm working with doesn't take too long. What I love best is that it fires off the test immediately after a
:w in vim, so I don't have to do any sort of context switching to run the test. It can get annoying though, and modifying your
~/.autotest to not run the entire suite after fixing a failed test helps cut that down. Another helpful thing that some of the thoughtbotters use is a zsh script that adds autocompletion for running unit and functional tests. Basically just type
tf, and tab away.
If you know of other testing tools that we should be looking into, speak up in the comments!