[rspec-users] I need some guidance

Scott Taylor mailing_lists at railsnewbie.com
Fri Dec 21 14:41:30 EST 2007


On Dec 21, 2007, at 1:30 PM, jimmy zimmerman wrote:

> I'm building an http communicator class for a web service API  
> wrapper and I'm trying to go through the BDD process in doing so.  
> I'm having a bit of a struggle figuring out how to set up the tests  
> in relation to isolating things appropriately as well as testing  
> behavior vs testing implementation.
>
> Specifically, I'm trying to set up a post method on my  
> HttpCommunicator so I have the following:
>
> describe FamilyTreeApi::HttpCommunicator, "post" do
> it "should accept an endpoint and an xml string to post" # I'm okay  
> with this one
> it "should perform post content to given endpoint"
> it "should return a response object"
> end
>
> I have written/passed the tests for the first spec, but I'm having  
> troubles figuring out how to verify behavior for the second and  
> third, and how to mock/stub it up. I'll be using the 'net/https'  
> standard libraries to implement the POST action, and I know that it  
> requires the use of a URI instance, a Net::HTTP instance, and a  
> Net::HTTP::Post request instance.
>

Yep - I was doing something like that just yesterday, excepting  
fetching a feed with the URI and Net:HTTP classes.

The way I got around it was to write one method which encapsulated  
the behaviour of the URI.parse (stubbing a class method which  
returned a mock).  The second method would wrap the first - so  
something like this:

def get_parsed_url
   URI.parse(url)
end

def get_feed
   Net::HTTP.get_response(get_parsed_url)
end

This allows me to stub out get_parsed_url when I'm testing the  
get_feed method, and ignore the URI class altogether.

Another way to get around this sort of thing is by passing mock  
objects into constructors, a la "Dependency Injection".  That would  
go something like this:

class Foo
   def initialize(http_class = Net::HTTP, uri_class = URI)
     @http_class, @uri_class = http_class, uri_class
   end

   def get_parsed_url
     @uri_class.parse(url)
   end

   def get_feed
     @http_class.get_response(get_parsed_url)
   end
end

This allows you to pass in mock objects (mock classes) for your  
tests, but it still allows the production code to default to what you  
really want (the Net::HTTP and URI classes).  It's also going to make  
your code more flexible, in the case that you ever wanted to swap out  
Net::HTTP or URI for an alternative implementation.

Hope that helps,

Scott



More information about the rspec-users mailing list