Stagger in disbelief if you’d like, but I’ve found an instance where it is more beneficial to not begin testing a piece of software until a measurable way through its development.
You no doubt remember my fabulous, in-development command-line NewsGator client, bulletin. I’ve extracted the NewsGator-specific code to a gem called WonderCroc. The README provides a small glimpse of what’s possible with the library now:
wondercroc
A gem that provides…
In any event, I haven’t written any tests for it yet. A lot of the initial programming was screwing around with Ruby’s RSS parser and running requests through in IRB. At this point, however, I’d like to start throwing specs at it as I refactor it and extend it to become a modular library.
So what?
Well, here’s the thing. I want to know that the library responds correctly to the XML responses sent by NewsGator’s server. I assume the correct way to test this is to mock the responses accurately. The NewsGator REST API documentation provides several partial samples, but they’re not complete or complex enough to give me a full range of parsing opportunities. So why not use actual responses?
So that’s my plan. Extend my WonderCroc::Client class to log the XML responses to a file, and use those in my mocks — something I can’t think of a good way to do if I hadn’t written the code in the first place.
Thoughts?
I’m not entirely sure what the status of this code is, but it seems to be working for me. I tried throwing it a little further upstream and got hit with 42 failed specs, so I’m content to use it as a spec helper for now.
In any event, yeah. There’s Hpricot goodness available for your Merb specs, it just has to be enabled. Where normally we’d get a controller response with a string containing the HTML of the body, we’ll get an Hpricot document that’s easily searchable for tags and such.
Here’s the helper:
spec/spec_helper.rb
def dispatch controller, action = :index, opts = {}
@controller = dispatch_to(controller, action, opts)
@controller.body = Merb::Test::ViewHelper::DocumentOutput.new @controller.body
end
Now in any of your specs, do this:
describe Main, "index action" do
it "should have a title of 'Happy Days Are Here'" do
dispatch Main, :index
@controller.body['title'].inner_text.should == "Happy Days Are Here"
end
end
Tada! No parsing through the document looking for the title tag. Very nice.
The methods you get are these:
[]: The index method will return a single element if there’s one of them in the document, or an Array of them if there’s multiple ones. Then you can pick out the data you want.
content_for: This method takes an HTML element or HTML element and class specification (like ‘div’ ‘div.content’) and returns the text contents of the first found tag that matches that element or element and class. This means if you have code like this:
<div class='awesome'>
This is some content.
<b>This is some more content.</b>
<a href='http://google.com'>This is a link.</a>
<ul>
<li>Item</li>
<li>Item</li>
</ul>
</div>
Then calling @controller.body.content_for('div.awesome') will return the literal interpretation of all of the text, indentations and all, like so:
This is some content.
This is some more content.
This is a link.
Item
Item
So be very careful doing matches with this method!
content_for_all: This method is the same as content_for, except it will parse through the entire document and find all of the elements that match that tag or tag and class, and return them in an Array. If you’re only looking for one instance of a tag, use content_for, as this will be needlessly time-consuming.
Edit: I should also mention that the [] method takes XPath expressions, so you could do something like div[@class='awesome']/ul/li to get an Array of the list items in the last example. Sweet!