Hey everyone, So here I am, trying to love my testing process-- but I really hit a wall today and experienced a moment where testing totally causes a lack of productivity... To make a long story short, here''s what happened: I''ve been integrating a 3rd party payment service within my app-- The way it works is, you get a token for the transaction, and then send a request to their API to see if the credit card authorizes, and then if it does, you can purchase it and make sure that''s valid... So I have a purchase model, and I am using state_machine to store the transaction state... For example, when an attempt to authorize a credit card is done, if that fails, then the state machine transitions within a failure block to set the purchase''s state to "authorization_failed". ... Ok... So, I made tests to cover all of this behavior, and everything was perfect-- all tests were passing, and then I was told: "Hey, we want to store the transaction data in the purchase records-- whether it failed or succeded"... I said: Sure! No Problem! The way I had this setup was, an attr_accessor :samurai_transaction and then a before_create callback that calls a method :build_samurai_transaction ... That method basically did: Samurai::Processor.authorize( ...bunch o'' auth data stuff... ) So to keep this data around, I made a migration to create the column (same name as the attr_accessor property) and then I added serialize :samurai_transaction and removed the attr_accessor macro. Everything should be exactly the same behaviorally.. Ran my tests to verify, and.. FAIL FAIL FAIL FAIL FAIL FAIL FAIL... Inspecting the failures, I see that all of a sudden I am getting bogus errors about "undefined method `matches_method?'' for nil:NilClass" I go through everything trying to figure out where this is coming from and finally find that it''s from my stubbing out the calls to Samurai.. I was doing: auth = stub("auth", :success? => true, :capture => true) Samurai::Processor.stubs(:authorize).returns auth .. It worked fine before, but apparently something can''t handle the serialized database column holding a mocked object? No idea... So I changed this to: auth = OpenStruct.new(:success? => true, :capture => true) The error went away, which then made me convinced that it is something to do with Mocha..... However, then I saw other tests failing, and guess why? WRONG NUMBER OF ARGUMENTS FOR CAPTURE (0 for 1). .... uhh? Samurai has it''s own ".capture" method to take funds.. This has some conflicts with Rails'' internal ".capture"......... For some reason, o = OpenStruct.new(:capture => true) o.capture => wrong number of arguments error... So now I have to do: o = OpenStruct.new(:kapture => true) def o.capture kapture end and, suddenly I am finding myself in hacky hack land-- and hating every minute of it. Not to mention my tests still are breaking because of some other undetermined problems having to do with OpenStruct not being as "simple" of a solution as I was hoping. ... AAAAAAAAAAAAAAAAAAAAAAGH! Patrick J. Collins http://collinatorstudios.com
On 1 May 2012 04:24, Patrick J. Collins <patrick at collinatorstudios.com>wrote:> Hey everyone, > > So here I am, trying to love my testing process-- but I really hit a > wall today and experienced a moment where testing totally causes a lack > of productivity... To make a long story short, here''s what happened: > > I''ve been integrating a 3rd party payment service within my app-- The > way it works is, you get a token for the transaction, and then send a > request to their API to see if the credit card authorizes, and then if > it does, you can purchase it and make sure that''s valid... > > So I have a purchase model, and I am using state_machine to store the > transaction state... For example, when an attempt to authorize a > credit card is done, if that fails, then the state machine transitions > within a failure block to set the purchase''s state to > "authorization_failed". > > ... Ok... So, I made tests to cover all of this behavior, and > everything was perfect-- all tests were passing, and then I was told: > "Hey, we want to store the transaction data in the purchase records-- > whether it failed or succeded"... I said: Sure! No Problem! > > The way I had this setup was, an attr_accessor :samurai_transaction > > and then a before_create callback that calls a method > :build_samurai_transaction > > ... That method basically did: > > Samurai::Processor.authorize( ...bunch o'' auth data stuff... ) > > So to keep this data around, I made a migration to create the column > (same name as the attr_accessor property) and then I added serialize > :samurai_transaction and removed the attr_accessor macro. > > Everything should be exactly the same behaviorally.. Ran my tests to > verify, and.. FAIL FAIL FAIL FAIL FAIL FAIL FAIL... > > Inspecting the failures, I see that all of a sudden I am getting bogus > errors about "undefined method `matches_method?'' for nil:NilClass" > > I go through everything trying to figure out where this is coming from > and finally find that it''s from my stubbing out the calls to Samurai.. > I was doing: > > auth = stub("auth", :success? => true, :capture => true) > Samurai::Processor.stubs(:authorize).returns auth > > .. It worked fine before, but apparently something can''t handle the > serialized database column holding a mocked object? No idea... > > So I changed this to: > auth = OpenStruct.new(:success? => true, :capture => true) > > The error went away, which then made me convinced that it is something > to do with Mocha..... However, then I saw other tests failing, and > guess why? > > WRONG NUMBER OF ARGUMENTS FOR CAPTURE (0 for 1). > > .... uhh? > > Samurai has it''s own ".capture" method to take funds.. This has some > conflicts with Rails'' internal ".capture"......... For some reason, > o = OpenStruct.new(:capture => true) > o.capture > => wrong number of arguments error... > > So now I have to do: > > o = OpenStruct.new(:kapture => true) > def o.capture > kapture > end > > and, suddenly I am finding myself in hacky hack land-- and hating every > minute of it. > > Not to mention my tests still are breaking because of some other > undetermined > problems having to do with OpenStruct not being as "simple" of a > solution as I was hoping. > > ... AAAAAAAAAAAAAAAAAAAAAAGH! > > > Patrick J. Collins > http://collinatorstudios.com > >The frustration your experiencing is actually a good thing, its your brain saying "hey something is just not right". Of course our natural reaction to this is just to get angry, but if we can get past that there is an opportunity to learn something important. In my limited experience I''ve found that listening to tests is one of the best ways to learn. There is probably something seriously wrong with your existing tests and code. So instead of trying to work around it, and get your tests to pass, seize the opportunity and try and work out why your tests and code suck and get to the bottom of the problem. This is a real opportunity to learn something. Unfortunately I''m not bright enough to tell you whats wrong, but applying general principles of good OO and good testing should help. In particular things like 1. ensuring your tests say what they are testing 2. ensure your unit tests only test responsibilities of their object instance 3. look for smells (e.g. tests that require lots of mocks are bad tests, and are indicative of highly coupled code), ... Finally an important lesson is that your tests being green is really not that important. Its much more important that tests 1. say what they do 2. are listened to, so they exert a positive influence on your design 3. make things easy to fix when they go red All best Andrew _______________________________________________> rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-- ------------------------ Andrew Premdas blog.andrew.premdas.org -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20120501/a9e0ccff/attachment.html>
> The frustration your experiencing is actually a good thing, its your brain saying "hey something is just not right". Of > course our natural reaction to this is just to get angry, but if we can get past that there is an opportunity to learn > something important. In my limited experience I''ve found that listening to tests is one of the best ways to learn. There > is probably something seriously wrong with your existing tests and code. So instead of trying to work around it, and get > your tests to pass, seize the opportunity and try and work out why your tests and code suck and get to the bottom of the > problem. This is a real opportunity to learn something.I hear you, but honestly these tests are very straight forward and not overly complicated in any way, shape, or form. I did some more experimenting and was able to narrow the problem down to: Serialization and Mocha. class Foo < ActiveRecord::Base serialize :bar end by using debugger within a test, I did:> foo = Foo.create! > foo.save=> true> foo.bar = stub("waka waka") > foo.save=> NoMethodError: undefined method `matches_method?'' for nil:NilClass from /Users/bountybuy/.rvm/gems/ruby-1.9.3-p125 at bountybuy/gems/mocha-0.10.5/lib/mocha/mock.rb:185:in `respond_to?'' ... So the solution since using OpenStruct as an alternative was a disaster is: Foo.class_eval { attr_accessor :bar } With that, ActiveRecord no longer handles the setter/getter methods for that column, and this issue goes away. AAAAAAAAAAAAGH! So I am out of testing hell now.. yay! Patrick J. Collins http://collinatorstudios.com
On Tue, May 1, 2012 at 2:17 PM, Patrick J. Collins <patrick at collinatorstudios.com> wrote:>> The frustration your experiencing is actually a good thing, its your brain saying "hey something is just not right". Of >> course our natural reaction to this is just to get angry, but if we can get past that there is an opportunity to learn >> something important. In my limited experience I''ve found that listening to tests is one of the best ways to learn. There >> is probably something seriously wrong with your existing tests and code. So instead of trying to work around it, and get >> your tests to pass, seize the opportunity and try and work out why your tests and code suck and get to the bottom of the >> problem. This is a real opportunity to learn something. > > I hear you, but honestly these tests are very straight forward and not > overly complicated in any way, shape, or form. ?I did some more > experimenting and was able to narrow the problem down to: ?Serialization and Mocha.Tests have to run code. Your tests are running already complicated code (Rails), that is further complicated by introducing Serialization, and then further complicated by using Mocha. I do these things too, so I''m not saying they are bad, but they are nothing if they are not un-straight-forward and complicated. Just because syntax hides all that complexity from you doesn''t make it go away. FWIW, David