David Kahn
2011-Apr-28 23:37 UTC
[rspec-users] Mock/stub ActiveMerchant? (or other cascading/multiple inheritance situation)
I am a bit new to mocking. I am trying to stub the ActiveMerchant::Billing::PaypalGateway#authorize method but not clear how to do it. This is my code and spec. This is the pertinent code: module Payment def gateway ActiveMerchant::Billing::PaypalGateway.new( ... ) end def authorize_payment(payment_info, associated_record_type, associated_record_id) gateway.authorize(payment_info.amount ...... end I tried this: ActiveMerchant::Billing::PaypalGateway.should_receive(:authorize).and_return(authorize_payment_success_response) And it does work on its own, but when I call Payment.gateway.authorize, ActiveMerchant still goes out to the web. I perhaps am missing understanding something -- yes, calling gateway.authorize does create a new ActiveMerchant::Billing::PaypalGateway, but I thought by stubbing the class I should be ok. Or alternatively is there a way I can do something like: Payment.gateway.authorize.should_receive(:authorize).and_return(nil) I am guessing this question does not have to specifically do with ActiveMerchant but how to mock and stub through cascading levels of classes/objects. David -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110428/1c419ebb/attachment.html>
Pat Maddox
2011-Apr-29 01:03 UTC
[rspec-users] Mock/stub ActiveMerchant? (or other cascading/multiple inheritance situation)
On Apr 28, 2011, at 4:37 PM, David Kahn wrote:> I am a bit new to mocking. I am trying to stub the ActiveMerchant::Billing::PaypalGateway#authorize method but not clear how to do it. This is my code and spec. > > This is the pertinent code: > > module Payment > def gateway > ActiveMerchant::Billing::PaypalGateway.new( > ... > ) > end > > def authorize_payment(payment_info, associated_record_type, associated_record_id) > gateway.authorize(payment_info.amount ...... > end > > I tried this: > ActiveMerchant::Billing::PaypalGateway.should_receive(:authorize).and_return(authorize_payment_success_response)This is setting an expectation on the PaypalGateway object (which is a class!). But when you call PaypalGateway.new, you get back an instance - which is where you want to set the expectation. So what you really need to stub is something that looks more like an instance...you''d start off with: gateway = stub(''gateway'') gateway.should_receive(:authorize) and next you can either stub PaypalGateway.new: ActiveMerchant::Billing::PaypalGateway.stub(:new).and_return gateway or what I''d more likely do: Payment.stub(:gateway).and_return gateway either one will get you there. Pat
David Kahn
2011-Apr-29 16:15 UTC
[rspec-users] Mock/stub ActiveMerchant? (or other cascading/multiple inheritance situation)
On Thu, Apr 28, 2011 at 8:03 PM, Pat Maddox <patmaddox at me.com> wrote:> On Apr 28, 2011, at 4:37 PM, David Kahn wrote: > > > I am a bit new to mocking. I am trying to stub the > ActiveMerchant::Billing::PaypalGateway#authorize method but not clear how to > do it. This is my code and spec. > > > > This is the pertinent code: > > > > module Payment > > def gateway > > ActiveMerchant::Billing::PaypalGateway.new( > > ... > > ) > > end > > > > def authorize_payment(payment_info, associated_record_type, > associated_record_id) > > gateway.authorize(payment_info.amount ...... > > end > > > > I tried this: > > > ActiveMerchant::Billing::PaypalGateway.should_receive(:authorize).and_return(authorize_payment_success_response) > > This is setting an expectation on the PaypalGateway object (which is a > class!). But when you call PaypalGateway.new, you get back an instance - > which is where you want to set the expectation. So what you really need to > stub is something that looks more like an instance...you''d start off with: > > gateway = stub(''gateway'')gateway.should_receive(:authorize)> > and next you can either stub PaypalGateway.new: > ActiveMerchant::Billing::PaypalGateway.stub(:new).and_return gateway > > or what I''d more likely do: > Payment.stub(:gateway).and_return gateway >Thanks Pat, this worked great and I think helping get my head around doing this. I do have one additional question... I am testing a module here, and noticed that I had to both include the module (Payment) *and* in my spec call Payment#authorize_payment to get things working with the stub. It seems kind of strange as if I did not include the module at the top, then I would get ''undefined method `authorize_payment'' for Payment:Module'' when called in the spec, which makes sense. But what does not make sense is that when I do include the Module at the top, I still have to call Payment#authorize_payment and not just authorize_payment to get the stub to take (the test passes in both cases, when not explicitly declaring Payment#... the mock does not take). So it seems that it is as if there are two versions of Payment module --- one which is explicitly connected via the rspec stub, and the other which is the native. Once I stub Payment explicitly, I must explicitly declare it on my calls, otherwise it goes to the native code. Is this correct? I thought if I just removed the line ''Payment.stub(:gateway).and_return(gateway)'' that I would not have to call Payment#authorize.... and instead use authorize... but again, in this case the mock does not take. Anyway, I have things working (as below), but interested in why this is so. require ''spec_helper'' include Payment describe Payment do before(:each) do gateway = stub(''gateway'') Payment.stub(:gateway).and_return(gateway) gateway.stub!(:authorize).and_return(gateway_authorize_success_response) end it "should authorize payment with paypal using a valid card" do response = Payment.authorize_payment(payment_info_success, ''Bet'', 1000) ... end I.E., why does this not hit the stub: it "should authorize payment with paypal using a valid card" do response = authorize_payment(payment_info_success, ''Bet'', 1000) ... end> > either one will get you there. > > Pat > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110429/8f371981/attachment.html>
Rodrigo Rosenfeld Rosas
2011-Apr-30 03:05 UTC
[rspec-users] Mock/stub ActiveMerchant? (or other cascading/multiple inheritance situation)
Em 29-04-2011 13:15, David Kahn escreveu:> > > On Thu, Apr 28, 2011 at 8:03 PM, Pat Maddox <patmaddox at me.com > <mailto:patmaddox at me.com>> wrote: > > On Apr 28, 2011, at 4:37 PM, David Kahn wrote: > > > I am a bit new to mocking. I am trying to stub the > ActiveMerchant::Billing::PaypalGateway#authorize method but not > clear how to do it. This is my code and spec. > > > > This is the pertinent code: > > > > module Payment > > def gateway > > ActiveMerchant::Billing::PaypalGateway.new( > > ... > > ) > > end > > > > def authorize_payment(payment_info, associated_record_type, > associated_record_id) > > gateway.authorize(payment_info.amount ...... > > end > > > > I tried this: > > > ActiveMerchant::Billing::PaypalGateway.should_receive(:authorize).and_return(authorize_payment_success_response) > > This is setting an expectation on the PaypalGateway object (which > is a class!). But when you call PaypalGateway.new, you get back an > instance - which is where you want to set the expectation. So what > you really need to stub is something that looks more like an > instance...you''d start off with: > > gateway = stub(''gateway'') > > gateway.should_receive(:authorize) > > and next you can either stub PaypalGateway.new: > ActiveMerchant::Billing::PaypalGateway.stub(:new).and_return gateway > > or what I''d more likely do: > Payment.stub(:gateway).and_return gateway > > > > Thanks Pat, this worked great and I think helping get my head around > doing this. > > I do have one additional question... I am testing a module here, and > noticed that I had to both include the module (Payment) *and* in my > spec call Payment#authorize_payment to get things working with the stub. > > It seems kind of strange as if I did not include the module at the > top, then I would get ''undefined method `authorize_payment'' for > Payment:Module'' when called in the spec, which makes sense. But what > does not make sense is that when I do include the Module at the top, I > still have to call Payment#authorize_payment and not just > authorize_payment to get the stub to take (the test passes in both > cases, when not explicitly declaring Payment#... the mock does not take). > > So it seems that it is as if there are two versions of Payment module > --- one which is explicitly connected via the rspec stub, and the > other which is the native. Once I stub Payment explicitly, I must > explicitly declare it on my calls, otherwise it goes to the native > code. Is this correct? > > I thought if I just removed the line > ''Payment.stub(:gateway).and_return(gateway)'' that I would not have to > call Payment#authorize.... and instead use authorize... but again, in > this case the mock does not take. > > Anyway, I have things working (as below), but interested in why this > is so. > > require ''spec_helper'' > include Payment > > describe Payment do > > before(:each) do > gateway = stub(''gateway'') > Payment.stub(:gateway).and_return(gateway) > > gateway.stub!(:authorize).and_return(gateway_authorize_success_response) > end > > it "should authorize payment with paypal using a valid card" do > response = Payment.authorize_payment(payment_info_success, ''Bet'', > 1000) > ... > end > > I.E., why does this not hit the stub: > > it "should authorize payment with paypal using a valid card" do > response = authorize_payment(payment_info_success, ''Bet'', 1000) > ... > endThat is what happens in Rspec behind the scenes: module A def test 1 end end include A puts test # 1 puts A.test # 1 puts singleton_methods.include?(:test) # false puts A.singleton_methods.include?(:test) # false class << A def test 2 def end puts test # 1 puts A.test # 2 puts singleton_methods.include?(:test) # false puts A.singleton_methods.include?(:test) # true If you don''t know very much about singleton classes (some people call them anonymous classes), you can take a look at these articles: http://www.contextualdevelopment.com/articles/2008/ruby-singleton http://ola-bini.blogspot.com/2006/09/ruby-singleton-class.html Hope that helps understanding why you can''t include a mocked module in your spec (actually you can, but it won''t work as expected...) Cheers, Rodrigo. -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110430/b0644eec/attachment.html>
David Kahn
2011-Apr-30 05:23 UTC
[rspec-users] Mock/stub ActiveMerchant? (or other cascading/multiple inheritance situation)
On Fri, Apr 29, 2011 at 10:05 PM, Rodrigo Rosenfeld Rosas < lbocseg at yahoo.com.br> wrote:> Em 29-04-2011 13:15, David Kahn escreveu: > > > > On Thu, Apr 28, 2011 at 8:03 PM, Pat Maddox <patmaddox at me.com> wrote: > >> On Apr 28, 2011, at 4:37 PM, David Kahn wrote: >> >> > I am a bit new to mocking. I am trying to stub the >> ActiveMerchant::Billing::PaypalGateway#authorize method but not clear how to >> do it. This is my code and spec. >> > >> > This is the pertinent code: >> > >> > module Payment >> > def gateway >> > ActiveMerchant::Billing::PaypalGateway.new( >> > ... >> > ) >> > end >> > >> > def authorize_payment(payment_info, associated_record_type, >> associated_record_id) >> > gateway.authorize(payment_info.amount ...... >> > end >> > >> > I tried this: >> > >> ActiveMerchant::Billing::PaypalGateway.should_receive(:authorize).and_return(authorize_payment_success_response) >> >> This is setting an expectation on the PaypalGateway object (which is a >> class!). But when you call PaypalGateway.new, you get back an instance - >> which is where you want to set the expectation. So what you really need to >> stub is something that looks more like an instance...you''d start off with: >> >> gateway = stub(''gateway'') > > gateway.should_receive(:authorize) >> >> and next you can either stub PaypalGateway.new: >> ActiveMerchant::Billing::PaypalGateway.stub(:new).and_return gateway >> >> or what I''d more likely do: >> Payment.stub(:gateway).and_return gateway >> > > > Thanks Pat, this worked great and I think helping get my head around doing > this. > > I do have one additional question... I am testing a module here, and > noticed that I had to both include the module (Payment) *and* in my spec > call Payment#authorize_payment to get things working with the stub. > > It seems kind of strange as if I did not include the module at the top, > then I would get ''undefined method `authorize_payment'' for Payment:Module'' > when called in the spec, which makes sense. But what does not make sense is > that when I do include the Module at the top, I still have to call > Payment#authorize_payment and not just authorize_payment to get the stub to > take (the test passes in both cases, when not explicitly declaring > Payment#... the mock does not take). > > So it seems that it is as if there are two versions of Payment module --- > one which is explicitly connected via the rspec stub, and the other which is > the native. Once I stub Payment explicitly, I must explicitly declare it on > my calls, otherwise it goes to the native code. Is this correct? > > I thought if I just removed the line > ''Payment.stub(:gateway).and_return(gateway)'' that I would not have to call > Payment#authorize.... and instead use authorize... but again, in this case > the mock does not take. > > Anyway, I have things working (as below), but interested in why this is so. > > require ''spec_helper'' > include Payment > > describe Payment do > > before(:each) do > gateway = stub(''gateway'') > Payment.stub(:gateway).and_return(gateway) > > gateway.stub!(:authorize).and_return(gateway_authorize_success_response) > end > > it "should authorize payment with paypal using a valid card" do > response = Payment.authorize_payment(payment_info_success, ''Bet'', 1000) > ... > end > > I.E., why does this not hit the stub: > > it "should authorize payment with paypal using a valid card" do > response = authorize_payment(payment_info_success, ''Bet'', 1000) > ... > end > > > That is what happens in Rspec behind the scenes: > > module A > def test > 1 > end > end > > include A > > puts test # 1 > puts A.test # 1 > puts singleton_methods.include?(:test) # false > puts A.singleton_methods.include?(:test) # false > > class << A > def test > 2 > def > end > > puts test # 1 > puts A.test # 2 > puts singleton_methods.include?(:test) # false > puts A.singleton_methods.include?(:test) # true > > If you don''t know very much about singleton classes (some people call them > anonymous classes), you can take a look at these articles: > > http://www.contextualdevelopment.com/articles/2008/ruby-singleton > http://ola-bini.blogspot.com/2006/09/ruby-singleton-class.html > > Hope that helps understanding why you can''t include a mocked module in your > spec (actually you can, but it won''t work as expected...) >Thanks Rodrigo... I think I see it. In the end I have discovered that if I want to mock a module, rather than going directly I need to stub out methods in the class I am testing which call the module and this seems to work, otherwise (and I am not sure if this is exactly the case you are explaining), but certainly it ''does not work as expected''.> > Cheers, > > Rodrigo. > > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110430/ae29c674/attachment-0001.html>