[rspec-users] Mocking base class methods

Zach Dennis zach.dennis at gmail.com
Fri Mar 20 10:18:51 EDT 2009


2009/3/20 Jeroen van Dijk <jeroentjevandijk at gmail.com>:
> Huray, got it solved! Hopefully you'll agree with this solution (thanks to
> mocha sorry)
>
>
>   describe "count_by_params" do
>     it "should pass all the params and options to #scope_by_params" do
>       params  = { :foo => 1, :bar => 2 }
>       options = { :foo => 3, :bar => 4 }
>
>       @base = ActiveRecord::Base
>       @result = mock("query results", :count => 1)
>       @base.expects(:scoped_by_params).with(params,
> options).returns(@result)
>       @base.count_by_params(params, options)
>     end
>

Is #scoped_by_params a public method that has its own examples? Down
at the model level I would want to know that count_by_params actually
was working as expected and not just collaborating with itself to call
an internal method. I don't know how crazy scoped_by_params is so
maybe there is a potential risk you're willing to accept given the
complexity of what it does, but this example raises my eyebrow as to
the behaviour your actually expecting and verifying.


>
> On Thu, Mar 19, 2009 at 5:35 PM, Jeroen van Dijk
> <jeroentjevandijk at gmail.com> wrote:
>>
>> Hi all,
>>
>> I'm having the same problem as Tobi and I wanted to try Tobi mock solution
>> but unfortunately it does not work for me.
>>
>> Here is a trivial example I want to test:
>>
>> module ActiveRecord
>>   class Base
>>       def self.count_by_params(params = {}, options = {})
>>           scoped_by_params(params).count
>>       end
>>   end
>> end
>>
>> And my spec:
>> module ActiveRecord
>>   class Base
>>     include BaseClassMock
>>   end
>> end
>>
>> class DummyUser < ActiveRecord::Base
>> end
>>
>> it "should pass all the params and options to #scope_by_params" do
>>    params  = { :foo => 1, :bar => 2 }
>>     options = { :foo => 3, :bar => 4 }
>>     @dummy = DummyUser
>>     @dummy.base_class.should_receive(:scope_by_params).with(params,
>> options)
>>     DummyUser.count_by_params(params, options)
>> end
>>
>> With the above example and Tobi's BaseClassMock module I'm getting
>> "undefined method `should_receive' for #<Object:0x188c164>". Btw, I also get
>> this when I do not use Tobi be it for for #<Class:0x188cdf8> instead of
>> Object.
>>
>> Any suggestions on what I'm doing wrong or a different approach?
>>
>> Cheers,
>> Jeroen
>>
>>
>>
>> On Sat, Feb 21, 2009 at 10:24 PM, Tobi <listaccount at e-tobi.net> wrote:
>>>
>>> Zach Dennis wrote:
>>>
>>> > +1 to composition over inheritance here. Mocking at different layers
>>> > of an inheritance hierarchy sounds like trouble and screams to pull
>>> > that thing apart.
>>>
>>> Good point! I've already tried the composition approach. It solves the
>>> testabilitiy problems, but it doesn't feel right in this case.
>>>
>>> Inheritance seems to be the more natural approach. The C++ application
>>> will call virtual methods on the base class which should be overriden in
>>> a
>>> derived class. So it seems to make sense to have the same inheritance
>>> tree
>>> in the Ruby counter parts.
>>>
>>> If I would use composition, I would at least need to dynamically extend
>>> the referenced base class so I can override some of the methods that are
>>> the counter parts of the virtual C++ methods. And I would need to expose
>>> the referenced class to the C++ code.
>>>
>>> I've finally found a way to kinda mock the base class in some way:
>>>
>>> module BaseClassMock
>>>  attr_accessor :base
>>>
>>>  def self.included(klass)
>>>    class << klass
>>>      @@base_class = Object.new
>>>
>>>      def base_class
>>>        return @@base_class
>>>      end
>>>    end
>>>  end
>>>
>>>  def initialize(*args)
>>>    @@base_class.new(*args) if @@base_class.respond_to?(:new)
>>>    @base = Object.new
>>>  end
>>>
>>>  def method_missing(symbol, *args)
>>>    @base.send(symbol, *args)
>>>  end
>>> end
>>>
>>> Any base class (Swig classes in my case) should then be declared for
>>> RSpec
>>> like:
>>>
>>> module Swig
>>>  class Base; include BaseClassMock; end
>>> end
>>>
>>> And in the specs I can then do:
>>>
>>>    Derived.base_class.should_receive(:new).with('something')
>>>    d = Derived.new('something')
>>>
>>> or:
>>>
>>>    d = Derived.new
>>>    d.base.should_receive(:do_something)
>>>    d.do_something
>>>
>>> Tobias
>>> _______________________________________________
>>> rspec-users mailing list
>>> rspec-users at rubyforge.org
>>> http://rubyforge.org/mailman/listinfo/rspec-users
>>
>
>
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
>



-- 
Zach Dennis
http://www.continuousthinking.com
http://www.mutuallyhuman.com


More information about the rspec-users mailing list