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
- concisely and quickly defining high-level requirements
- being easy to read/understand
- separating testing dependence from other MVC elements
- 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
- 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?