[rspec-users] Mocking base class methods

Jeroen van Dijk jeroentjevandijk at gmail.com
Sat Mar 21 14:12:20 EDT 2009


> >   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.
>


scoped_by_params is indeed a public method with its own examples. I agree
that this spec does not say much, it is not more than a verification of its
implementation i guess. In sum, I have three methods find_by_params,
count_by_params and scoped_by_params. The first two rely on
scoped_by_params, which in turn relies on dynamic scopes from
ActiveRecord::Base.

I see two possible ways of testing this; an integration test in which we
focus on the result of the queries. Another possibility, the one I use in
this example, is to check if other methods are called correctly.

My reasoning is that ActiveRecord::Base has an extensive test suite, so I
only need to test that it is called correctly. By mocking methods call I'm
having simpler and faster tests. The problem is that the test by itself will
not tell me when Rails internals are changed and my code has actually been
broken. I'm assuming that the end result should be tested by integration
tests, do you agree?

What would your test strategy be?

(The code is actually part of a small plugin I'm writing and can be found
here
http://github.com/jeroenvandijk/find_by_params/blob/5a97028c505d9254b2bcbe5e3aa5b801f7ca34ca/lib/find_by_params.rb
)


Jeroen

PS I'm a TDD beginner so feel free correct me :)




> >
> > 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
> _______________________________________________
> 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/20090321/63af65ed/attachment.html>


More information about the rspec-users mailing list