I need some guidance

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.

I don’t want to hit the actual web service each time I run the test, so
I’d
like to stub or mock this in some way. I think I just need some guidance
on
how to approach something like this.

Do I just ignore the implementation details?
Do I push the actual posting of data to a private method and mock that
method so that I can verify that it is being called?
Would you recommend I actually hit the web service?

Any help is greatly appreciated.

On Dec 21, 2007 10:30 AM, jimmy zimmerman [email protected]
wrote:

this one
I don’t want to hit the actual web service each time I run the test, so I’d
Jimmy Zimmerman
http://jimmyzimmerman.com/blog/


rspec-users mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users

There’s a PeepCode screencast where he shows how to do something like
this I think…in the past, I’ve always used mocks to drive an
internal API that I like. Then when that’s more or less locked down,
I write a spec to implement that API. I pull response pages from the
external service, and stub Net::HTTP to return those. It’s a tad bit
brittle, because if the service changes then your specs will be
invalid. However by writing the client code and driving the API
first, you can at least guarantee that you keep a clean API that you
want, that remains decoupled from the external service.

Pat

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

it “should accept an endpoint and an xml string to post” # I’m okay
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

to stub or mock this in some way. I think I just need some
guidance on how to approach something like this.

Do I just ignore the implementation details?
Do I push the actual posting of data to a private method and mock
that method so that I can verify that it is being called?
Would you recommend I actually hit the web service?

Oh yeah - and also, you probably will want an integration test which
actually hits the web service at least once just to check that the
API hasn’t changed (and so on, if you can actually do it). But
that’s what the story runner is for (and not mock objects).

Scott

Thanks Pat,

When I wrote my first message, I think I was missing a big part of the
Mock/Stub capabilities. I thought you could only stub or mock methods of
a
particular instance that had been set up already in the spec. However, I
realize now that you can also stub/mock class methods.

I think my approach will be to create a mock object that would give
results
for method calls that I expect for Net::HTTP. I would then stub the new
call
on Net::HTTP to return that mock object. I would then set up
should_receive
verifications on that object to make sure that it’s being called
appropriately. That way, I’m not hitting the web service, but I can
verify
that things are happening correctly in my method. Does this approach
sound
right?

By the way, I have watched the Peepcode screencasts, and they have
helped
tremendously. I had just missed this (big) part of mocking/stubbing when
I
watched the Mocks video.


Jimmy