Rails Functional testing with Stubs and BDD

Posted by Brian in Rails, snacks, tips (July 26th, 2007)

One of the things that bothers many people about functional tests with Rails is the seemingly unnecessary duplication of test coverage.

Consider this test, a very common “create new user” functional test:

def test_create
   post :create, {:user=>{:username => "brian",
                         :password =>"1234", 
                         :password_confirmation=>"1234", 
                         :email=>"hoganbp@uwec.edu"}}
   assert assigns(:user)
   assert_redirected_to :action=>"list"
   
   
end

This is an example of test-driven development at the controller level. You pass in the POST parameters so that the controller can create the user object and save it to the database. This then triggers the redirect.

But this is really kinda bad. You already know that you can save records, you’ve got unit tests that prove that your validation works. You’re really interested in how the controller responds when it creates a valid record.

Behaviors

Behavioral driven development (BDD) is a technique receiving a lot of attention lately in the Rails community. The idea is to write your tests looking at the behavior you’re trying to test, as opposed to just testing methods.

Take a look at the behavior of the controller. What’s it supposed to do?

When a new user is created successfully, it should go to the list action.

So… let’s rename that scaffolded functional test to

   def test_should_redirect_to_list_when_user_is_created

   end

In BDD, you use words like should to specify what should happen. See, already your test is less ambiguous. Eventually you’re going to have to test that it “should_render_registration_form_when_creation_fails” or something like that.

Stubs

We already know from unit tests we wrote that we can save user records. We don’t need to test that again. Good tests don’t cover more than one piece of functionality anyway. How do we get around to it?

Enter http://rubyforge.org/projects/mocha/. Mocha is a mocking library. You can read more about it in the documentation, but I’ll show you how to use the stubbing features to bypass creating records.

Install mocha.

  gem install mocha

Open up your test/test_helper.rb and add

  require 'mocha'

to the top of the file.

This simple bit of code added to our test makes it all work:

   User.any_instance.stubs(:save).returns(true)

You would read that as “Any instance I create of User should have its save method always return true when I call it.”

That means our functional test becomes

def test_should_redirect_to_list_when_user_is_created
   User.any_instance.stubs(:save).returns(true)
   post :create
   assert_redirected_to :action=>"list"
   
end

Wrapping up

Stubbing helps you decouple your methods, and BDD helps you think about your process rather than your code. I encourage you to read more about both. Keep in mind you still want to test other aspects of your controllers, and sometimes you might want to ensure that certain things work certain ways. For example, you might still need @user to contain real data when you do searches so your views will render properly. This is just an example of the power of stubbing.

Leave a reply

:mrgreen: :neutral: :twisted: :shock: :smile: :???: :cool: :evil: :grin: :oops: :razz: :roll: :wink: :cry: :eek: :lol: :mad: :sad: