At the Forge - Testing Rails Applications with Shoulda

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

Already, you probably can see how Shoulda macros can reduce the amount of code you need to write. Shoulda also provides an RSpec-like facility that makes it possible to name tests using strings, rather than method names. Granted, this is now included in Test::Unit, albeit using a slightly different syntax. But, you can define tests using the should keyword, rather than test, which adds a bit of readability—especially when used in conjunction with contexts, which I describe below.

Here, I create a single method in the model, fullname, which returns the concatenation of the person's first and last name:

def fullname  # added to app/models/person.rb
  "#{firstname} #{lastname}"
end

Next, I add a new test:

should "return the concatenation of the first and last name" do
  person = Person.new(:firstname => "First",
                      :lastname => "Last",
                      :email_address => "email@example.com")

  assert_equal person.fullname, "First Last"
end

Now, there's nothing wrong with this test. It not only passes, but it also does a good job of checking that you are getting the right values back. Maybe it's just me, but I sometimes end up with very long lists of tests and end up categorizing them using comments inside the test file. Shoulda provides contexts that let you group tests within your file, using code rather than comments. It's obviously a bit silly to have a single context and a single test, but as with many things in the TDD/BDD world, it's worth doing things right even from the beginning, because you know that your codebase will grow over time, making it difficult to organize things correctly.

To define a context, you merely write:

context "Defined methods" do
    # "should" blocks go here
end

In other words, you now can rewrite the test block as:

context "Defined methods" do
    should "return the concatenation of the first and last name" do
        person = Person.new(:firstname => "First",
                      :lastname => "Last",
                      :email_address => "email@example.com")

        assert_equal person.fullname, "First Last"
    end
end

With a context block and a should block, you now can read your test as, “Defined methods should return the concatenation of the first and last name.” It's not the most amazing description in the world, but it's not a bad start. And besides, now you can add additional should blocks to test other defined methods.

A context may contain other contexts, as well as should blocks. This means that if you have a particularly complicated model you want to test, you can have a hierarchy of contexts, with should blocks at the bottom.

Moreover, using a context block means that you can write a setup block, which defines variables and otherwise allocates resources that will be used inside a should block. You could, for instance, now write:

context "Defined methods" do
  setup do
    @person = Person.new(:firstname => "First",
                         :lastname => "Last",
                         :email_address => "email@example.com")

  end

  should "return the concatenation of the first and last name" do
    assert_equal @person.fullname, "#{@person.firstname}
    ↪#{@person.lastname}"
  end
end

As you can see, variables that are shared between a setup block and a should block need to be instance variables, their names preceded by an @ sign.

When a test is invoked, all the setup blocks within all of its surrounding contexts are invoked first. This means if a should block is within three nested contexts, and if each of those contexts has its own setup block, all three will fire before the test is executed.

Conclusion

If you are using Test::Unit to test your Ruby on Rails application, Shoulda is a natural fit, allowing you to write a large number of common tests using flexible, easy-to-read macros. In this article, I cover uses of Shoulda only for ActiveRecord models; other parts of Shoulda work with controller tests, providing additional features that can be of use for testers.

From my perspective, using Shoulda is a no-brainer. I have used it in a number of projects already and found that it further lowered the threshold to TDD/BDD, helping make my code that much more reliable. If you are new to testing, Shoulda is a great way to get started, providing an easy way to increase the stability and correctness of your code. All in all, Shoulda is a great resource for Ruby programmers in general and Rails programmers in particular.

______________________

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState