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. -- Jimmy Zimmerman http://jimmyzimmerman.com/blog/ -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/rspec-users/attachments/20071221/4610b75e/attachment.html
On Dec 21, 2007 10:30 AM, jimmy zimmerman <jimmy.zimmerman at gmail.com> 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. > > 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. > -- > Jimmy Zimmerman > http://jimmyzimmerman.com/blog/ > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > 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:> 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
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 On Dec 21, 2007 11:56 AM, Pat Maddox <pergesu at gmail.com> wrote:> On Dec 21, 2007 10:30 AM, jimmy zimmerman <jimmy.zimmerman at gmail.com> > 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. > > > > 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. > > -- > > Jimmy Zimmerman > > http://jimmyzimmerman.com/blog/ > > _______________________________________________ > > rspec-users mailing list > > rspec-users at rubyforge.org > > 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 > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-- Jimmy Zimmerman http://jimmyzimmerman.com -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/rspec-users/attachments/20071221/f13d7d36/attachment-0001.html
> 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