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:

config.gem "thoughtbot-shoulda", :lib => "shoulda", 
 ↪:source => "http://gems.github.com"

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:

rails simple
cd simple
./script/generate model Person firstname:text lastname:text
 ↪birthdate:date grade_in_school:integer phone_number:text 
 ↪email_address:text
rake db:migrate

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:


require 'test_helper'

class PersonTest < ActiveSupport::TestCase
  # Replace this with your real tests.
  test "the truth" do
    assert true
  end
end

True, you can invoke rake test on this, and the tests will succeed, but that's because the test is completely empty. You can write:

rake test:units

______________________

Webinar
One Click, Universal Protection: Implementing Centralized Security Policies on Linux Systems

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Learn More

Sponsored by Bit9

Webinar
Linux Backup and Recovery Webinar

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.

Learn More

Sponsored by Storix