Friday, July 18, 2008

RSpec; what methods and when to use them

In the true nature of a blog, my daily hikes will be somewhat random, driven by much of what I do at work and what tickles my interest at the time. At some point, I will consider consolidation in some organized fashion, but hey... I'm getting ahead of myself.

My current topic deals with RSpec. Suffice to say, I am novice at Ruby/Rails; I love it. You don't need yet another testimonial here, so I won't spend the keystrokes. I really like what I see of RSpec. I am a strong advocate for unit tests. I've spent the last 3 years doing JUnit tests and I will always write unit tests. When I was introduced to RSpec at work, it took me a little time to get my brain around how they worked. It's been somewhat of a challenge to find a comprehensive tutorial on how RSpec works, although there is something out there. A few that I've found useful include:

general RSpec documentation
- http://rspec.info/
- BDD definition and old install instructions for RSpec

tutorials for using RSpec
- an-introduction-to-rspec-part-i
- rspec-tutorial
- developing-a-rails-model-using-bdd-and-rspec-part-1
- rspec-plain-text-story-runner-on-a-fresh-rails-app

reasons for using BDD
- in particular, RSpec
- rspec-and-unit-tests-localizing.html

As I understand it, RSpec is good at
  1. concisely and quickly defining high-level requirements
  2. being easy to read/understand
  3. separating testing dependence from other MVC elements
    1. controller tests never have to touch models or views; you can start testing controller functionality and mock and stub out only the necessary elements of models and views that the controller would interact with in real life
  4. cut down on test repeat (DRY; Don't Repeat Yourself); don't run model tests in the controller when those test are already being run in the model tests

That being said, I think I must be too wet behind the ears still because there are still somethings that I am not fully grasping. There are a whole host of functions that may be used in RSpec test writing. I believe these functions are made available through some Module that is applied by the RSpec gem or plugins.
# example
article = mock_model(Article)
article.should_receive(:author).and_return("Joe")
article.should_receive(:text).and_return("this is the text of the article")
assigns[:article] = article
assigns[:articles] = [article]

One thing that has snagged me in the past is not clearly understanding which of these helper RSpec methods is to be used in a given context. There is a set of helpers that are appropriate to use with Controller tests, another set for View tests, etc. Some might work across all contexts, but I'm finding that not all do (and perhaps don't even make sense).

For example, yesterday I spent a lot of time trying to write RSpec tests for a Controller. I never could figure out why the test didn't work. I was trying to use the 'have_tag' helper.
response.should have_tag('div', /regexp matching expected content/)
I debugged the test and tried to analyze the response object. This object, I believe, is provided by Rails and has nothing to do with RSpec. The response object has a body attribute which I was expecting to contain the generated html that is returned by the application. What I found was the path to the resource instead.
describe "#index" do
it "should return a list of show times" do
get :index
debugger

response.should have_tag("li")
end
end
You will probably need to install support for the ruby debugger if you haven't already in order for the sample above to work. As you would expect, when the test is executed, execution will break when it reaches the line with the string debugger. And all this to explain how I discovered what the value of response.body was when running the Controller test, which was:
"show_times/index"
Why wasn't there html in there? I'm not sure, actually. One theory is that Rails hasn't actually got to the point where it has retrieved the html yet, it's only set where it will pull the content from. What ever the reason is, this whole situation eventually got me thinking, "Do I really want to have that test in the controller anyway?" Isn't that a test of the view element anyway? And we want to be DRY, don't we? Perhaps it is a good thing this test has never worked because it might just be teaching me to pay more attention to what tests I am using and where I am putting those tests.

In fact, when I ran the debugger in the View context, I was getting back html content in the response.body attribute. This would suggest that one should only use have_tag in a View RSpec test.

A good TRail guide to consult would probably be Introduction to Writing Examples using Spec::Rails. After all, when it doubt, read the directions, right?

2 comments:

Silent Sentinel said...

See subsequent post.

This isn't the only time that I've seen someone say that response.body has valid html content. But when I try and access the value of the response.body attribute, I'm still just getting the template location.

Is there an environment setting I'm missing? If the controller is stubbed up with methods/messages, could this interfere with response.body getting back the html I'm looking for?

One thing that may help is to understand the request lifecycle through the eyes of Rails

Anonymous said...

You want integrate_views.