At the Forge - RSpec
Last month, I covered Shoulda, a Ruby gem that allows you to test your code using a method called behavior-driven development. BDD, as it is known, is closely related to test-driven development (TDD), which has become increasingly popular during the past few years, particularly within the Ruby community.
In both BDD and TDD, you start to program by writing a test that the program should pass, if it's working correctly. Of course, because the program hasn't been written yet, the test will fail. You then write the smallest amount of code possible to ensure that the test passes. When that happens, you continue coding by writing another test. The fact that your code is tested completely gives you the confidence and flexibility to “refactor”, moving code around and joining it together, without having to worry about introducing new, subtle bugs.
BDD differs from TDD not in its overall method, but rather in its approach and semantics. BDD concentrates on how things look from the outside, rather than from the inside of the code. In the case of a Web application, this often means looking at things from the user's perspective, or if you're a consultant, from the customer's perspective. No longer are you testing the code—instead, you are checking that it meets its specifications. Thus, working with BDD requires that you constantly think of yourself as a consumer of a particular piece of code, and that you consider what it should do at each point, if it is to work correctly. I intentionally use the word should here, because as you will see, that is an especially important word in the RSpec vocabulary, and it appears in nearly every test.
RSpec has become quite popular among Ruby programmers in general and Rails programmers in particular. It also is closely tied to several other high-quality testing technologies, such as Cucumber and Celerity, which I will explore in coming months. And, although RSpec is not everyone's cup of tea, it is popular enough that you should expect to encounter it if you do any Ruby development. Moreover, it is often good to try something different, and RSpec definitely is different, providing a new way of looking at testing.
The home page for RSpec is rspec.info, which contains instructions for installing RSpec, either on its own or as part of a Rails application. I'm looking at a simple Rails application this month as an example, so you need to install both parts.
The first requirement is installing two Ruby gems, both of which are stored on the popular repository for open-source projects, GitHub. You can install these gems with:
sudo gem install rspec rspec-rails -V --source ↪http://gems.github.com/
(If you already have installed GitHub as a source for gem installations, you don't need to specify it in this command.)
Note that if you have older RSpec-related gems installed, such as rspec_generator or spicycode_rspec_extensions, you probably should remove them from your system. Current versions of RSpec handle these functions for you, and I have encountered problems and conflicts that disappeared when I removed those old gems.
Now that you have RSpec installed, let's create a new, simple Rails project. I often like to use an address book (and appointment calendar) for my examples, so let's create one:
rails --database=postgresql appointments
Remember, Rails assumes you have three databases for your application, one each for the development, test and production environments. The database parameters are defined in config/database.yml. I assume you are able to set these configuration parameters correctly. Although you don't necessarily need a production database for the purposes of this column, you will need both development and test databases.
Now you must tell the Rails application to include RSpec. There are plugins for RSpec, but I generally prefer to use gems when possible. Modern versions of Rails allow you to include gems in config/environment.rb by adding the following two lines:
config.gem "rspec", :lib => false, :version => ">= 1.2.0" config.gem "rspec-rails", :lib => false, :version => ">= 1.2.0"
With the gems in place, you now can put RSpec in place for your Rails application:
This creates a spec directory (parallel to the test directory, which it effectively replaces). The spec directory contains, by default, three files:
rcov.opts: setting options for running the Ruby coverage tool rcov when run from within RSpec.
rspec.opts: setting options for RSpec itself.
spec_helper.rb: a Ruby file containing global definitions and configurations for the individual specifications, much like test_helper.rb performs in Test::Unit.
With the spec directory in place, you can begin to use the special RSpec generators for models, controllers and scaffolds. For example, you normally would generate a person model with:
./script/generate model person first_name:text last_name:text
This still will work, but any automatically generated tests will use Test::Unit, installing files into the test directory. By contrast, you can use:
./script/generate rspec_model person first_name:text last_name:text
This creates the same model file, but also creates a skeleton set of RSpec tests.