undefined method `matches_method?' for nil:NilClass

225 views
Skip to first unread message

patrick99e99

unread,
Apr 30, 2012, 10:22:21 PM4/30/12
to mocha-developer
Hi everyone,

This is the second time I have come across this problem, and so I
thought I would post and see if anyone has any ideas as to why this is
happening.

Basically I have a before_create method in my model that sets an
attribute on a model with something returned from a webservice...

class Purchase < ActiveRecord::Base
before_create :set_samurai

def set_samurai
self.samurai_transaction = Samurai::Processor.authorize( ... <auth
stuff> ...)
end
end

I have a test that is then doing:

...

def stub_authorization(options = {})
authorization = stub("auth", options)
Samurai::Processor.stubs(:authorize).returns authorization
end

context "samurai transaction" do

it "builds a samurai authorization transaction" do
stub_authorization(:lol => "lmao")
purchase.samurai_transaction.lol.should == "lmao"
end

...

This test fails giving me:
undefined method `matches_method?' for nil:NilClass

... And after a lot of digging, I finally found this was caused by
the stub("auth", options) line... I have no idea why though.

If I change that helper method to:
Hi everyone,

This is the second time I have come across this problem, and so I
thought I would post and see if anyone has any ideas as to why this is
happening.

Basically I have a before_create method in my model that sets an
attribute on a model with something returned from a webservice...

class Purchase < ActiveRecord::Base
before_create :set_samurai

def set_samurai
self.samurai_transaction = Samurai::Processor.authorize( ... <auth
stuff> ...)
end
end

I have a test that is then doing:

...

def stub_authorization(options = {})
authorization = OpenStruct.new(options)
Samurai::Processor.stubs(:authorize).returns authorization
end

Then everything works..... I had the same problem come up before when
I was trying to stub out Geokit's multi geocoder, and also had to
resort to using an OpenStruct object instead of a mock... Can anyone
tell me why this is happening?

-patrick

floehopper

unread,
May 1, 2012, 5:47:20 AM5/1/12
to mocha-d...@googlegroups.com
  def stub_authorization(options = {})
    authorization = OpenStruct.new(options)
    Samurai::Processor.stubs(:authorize).returns authorization
  end

Then everything works.....  I had the same problem come up before when
I was trying to stub out Geokit's multi geocoder, and also had to
resort to using an OpenStruct object instead of a mock...  Can anyone
tell me why this is happening?

-patrick

Hi Patrick,

Can you tell us about your environment i.e. what versions of Mocha, Rails, Ruby, what OS, how are you loading Mocha (e.g. Bundler, Rails plugin, etc)?

Also can you post a more complete version of the code under test and the test itself (ideally in a syntax-highlighted form e.g. using Gist [1]). For example, it's not clear from your message whether you have actually *created* a Purchase in your test.

Really what we need is all the code necessary to replicate the problem locally for ourselves.

Thanks, James.
----

patrick99e99

unread,
May 1, 2012, 3:06:26 PM5/1/12
to mocha-developer
Hi James,

Here is my gemfile...
https://gist.github.com/2570515

I just did some experimenting and narrowed the problem down to
serialization...

If I do:

class Purchase < ActiveRecord::Base
serialize :txn
end

And then from the context of my rspec tests, launch pry as a debugger:

[1] pry(#<RSpec::Core::ExampleGroup::Nested_1::Nested_1>)> p =
Purchase.create!
=> #<Purchase id: 1, created_at: "2012-05-01 19:03:44", updated_at:
"2012-05-01 19:03:44", txn: nil>
[2] pry(#<RSpec::Core::ExampleGroup::Nested_1::Nested_1>)> p.save
=> true
[3] pry(#<RSpec::Core::ExampleGroup::Nested_1::Nested_1>)> p.txn =
stub("lol")
=> #<Mock:lol>
[4] pry(#<RSpec::Core::ExampleGroup::Nested_1::Nested_1>)> p.save
NoMethodError: undefined method `matches_method?' for nil:NilClass
from /Users/bountybuy/.rvm/gems/ruby-1.9.3-p125@bountybuy/gems/
mocha-0.10.5/lib/mocha/mock.rb:185:in `respond_to?'
[5] pry(#<RSpec::Core::ExampleGroup::Nested_1::Nested_1>)>

-patrick

James Mead

unread,
May 2, 2012, 5:42:52 AM5/2/12
to mocha-d...@googlegroups.com
Hi Patrick,

I have managed to reproduce the problem you are seeing. The problem is that when you save your instance of Purchase with the stub set on the txn attribute, you are trying to serialize & deserialize the stub object. Unfortunately in the process of de-serializing Mocha#Mock#respond_to? is called before the instance has been properly re-hydrated (i.e. no instance variables have been set) and so you see the NoMethodError exception.

I've pushed up a potential fix [1] into a branch, but I want to have a think about whether this is actually a sensible solution. As I mention in the commit note, I'm not at all sure it's sensible to allow mock objects to be serialized and deserialized at all. If I can find a way to hook into serialization in a sensible way, I may just change Mocha to fail-fast when a mock object is serialized.

Can you explain a bit more why you want to stub the Purchase#txn attribute and in particular why you want to persist this stub object to the database? I'm sure there is a better way to achieve what you want, but I'm not sure we have enough details to go on.

Regards, James.


patrick99e99

unread,
May 9, 2012, 6:47:15 PM5/9/12
to mocha-developer
Hi James,

Well... So I have a purchase model that integrates with a 3rd party
payment service. The model has a before_create callback which
contacts the 3rd party service and gets a transaction object from
them, this object is serialized and stored in the model. This
transaction object responds to methods "success?" and "capture"...
So, I have some state_machine stuff in the purchase model that does
various things IF that object is success? and if capture returns true
etc.. So when I was writing specs, I obviously don't really want to
be hitting the API of the 3rd party service, so I stubbed that out and
wanted it to just return a fake transaction object that has success
and capture methods that I could make true/false depending on what I
was testing. So that's why... I am open to alternative approaches/
ideas if you have any.

-patrick
> [1]https://github.com/floehopper/mocha/commit/d0569869da15fa9d3d86b18047...

patrick99e99

unread,
May 9, 2012, 8:42:41 PM5/9/12
to mocha-developer
BTW, my current workaround was to simply do in my test's before block:

30 Purchase.class_eval do
31 attr_accessor :txn
32 end

That way, it skips storing it in the db, and I didn't really need to
test anything to do with retrieving that object.

-patrick

James Mead

unread,
May 14, 2012, 3:58:16 AM5/14/12
to mocha-d...@googlegroups.com
Hi Patrick,

I'm sorry I haven't had a chance to look into this any further. I've created a pull request [1] to provide a place to discuss the issue further.

Out of interest, did you try using the version of Mocha in the "stub-serialization" branch [2] that I mentioned? It would be useful to know whether this solved your problem.

Regards, James.



--
You received this message because you are subscribed to the Google Groups "mocha-developer" group.
To post to this group, send email to mocha-d...@googlegroups.com.
To unsubscribe from this group, send email to mocha-develop...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/mocha-developer?hl=en.


Reply all
Reply to author
Forward
0 new messages