At the Forge - Testing Rails Applications with Shoulda

New to testing? Just want an easier time with Test::Unit? Shoulda is the answer.

The past few months, I've been looking at a number of tools
that make it easier for Ruby on Rails developers to improve the
reliability of their software using automated testing. Even if you
don't fully subscribe to the notion of test-driven development (TDD)
or its cousin, behavior-driven development (BDD), the fact that Rails
makes it so easy to test each part of your code makes it less likely
that foolish mistakes will creep into your applications.

By default, Rails comes with Test::Unit, a test suite that makes it
possible, and even easy, to check your code. Coupled with the test
classes that come with actionpage, one of the core Ruby gems that
comes with Rails, you can create a comprehensive test suite at the
unit (model), functional (controller) and integration
(cross-controller) levels. If you have a comprehensive test
suite, you easily will detect, and understand the implications
of, changes you make to the code that break the test.

That said, Test::Unit sometimes can be a bit verbose and repetitive.
If you are writing unit tests, and you want to make sure that a
particular attribute has been tested completely, it would be nice to
be able to express a number of test cases quickly and tersely. Tests
can function, in many ways, as a type of specification (as I will explain
when we get to RSpec and Cucumber in coming months), and the easier it
is to read these specifications, the less likely odd behavior is to
slip through the cracks. It also goes without saying that the easier
it is to write tests, and particularly comprehensive tests, the more
likely you are to write them.

This is why Shoulda, a set of macros that work with Test::Unit, has
become popular among Ruby developers in general and Rails
developers in particular. Shoulda, developed by Tammer Saleh, a
programmer who works for the Thoughtbot consulting company in Boston,
is a set of macros that makes it easier to write tests with Test::Unit,
as well as easier to read them. I have begun to use Shoulda with
projects that I test with Test::Unit and have found it to be quite
enjoyable.

This month, I take a look at Shoulda, and how you can integrate its
macros into the tests you write in a Rails application. I explain
how Shoulda divides tests into contexts, allowing you to group
tests together even within a single file. I also describe how
Shoulda's various macros make it easy to run a number of tests using a
single readable line.

I should note that although Shoulda originally was designed to be used
with Test::Unit and to provide something of an RSpec-like environment
for Test::Unit users, it adds a growing amount of support for
RSpec as well. Even if you use RSpec, you might want to consider
using Shoulda along with your standard RSpec tests (or specs). I
haven't looked at the combination for my own work, but it might be
appropriate for what you're doing.

Installation and Basic Use

Shoulda comes packaged as a Ruby gem, and can be installed as:

sudo gem install thoughtbot-shoulda --source=http://gems.github.com

Earlier versions of Shoulda came packaged under a slightly different
name (Shoulda, rather than thoughtbot-shoulda). It also
is possible to install Shoulda as a Rails plugin; in this article, I
assume that you have installed the gem version.

You can incorporate the gem in your configuration file,
config/environment.rb:

With that in place, your Rails application either will run with
Shoulda in place, or it will fail to load, complaining that the gem
has not been installed. In one of my favorite Rails functions, you
then can type:

rake gems:install

and your Rails application will examine its list of required gems,
download those that are not yet on the system and install them in the
appropriate places.

Let's assume you have created a simple Rails application that
contains a single model that describes people. You can create it in
the following way:

At this point, you now have a simple Rails application (using the
built-in default database, SQLite) with a single model defined. By
creating your model with a generator, you get the following simple unit
test file: