Mocking base class methods


#1

Hi!

I need to write some wrapper classes, that derive from SWIG generated
proxy classes for some C/C++ extensions.

The Ruby wrapper classes need to do a lot of ‘super’ calls to the base
classes generated by SWIG.

The question is: Is there any way to make RSpec mock a base class and
record/verify that certain methods on a base class are called?

As long as the methods in the derived class have different names this is
no problem, but otherwise it doesn’t seem to be possible - at least not
with RSpec’s mocking/stubbing facilities, does it?

I see other ways to do this, like wrapping an instance of the Swig class
instead of deriving from it or writing some custom mocking code that
fakes
the base class e.g. with define_method, but I’m especially asking if
this
is possible with RSpec.

thx,

Tobias

module Swig
class Base
def initialize(arg)
end

def do_something(arg)
end

end
end

module Wrapper
class Derived << Swig::Base
def initialize(arg)
super(arg)
yield(self) if block_given?
end

def do_something(arg)
  do_something(arg)
  # additional behaviour here
end

end
end

describe Wrapper::Derived do
it ‘should initialize the base class with the same argument’ do
# No idea, how to mock Base.initialize() here
end

it ‘should do something with the base class when doing something’ do
# No idea, how to mock Base.do_something() here
end
end


#2

On Sat, Feb 21, 2009 at 6:31 AM, Tobi removed_email_address@domain.invalid wrote:

As long as the methods in the derived class have different names this is
no problem, but otherwise it doesn’t seem to be possible - at least not
with RSpec’s mocking/stubbing facilities, does it?

I see other ways to do this, like wrapping an instance of the Swig class
instead of deriving from it or writing some custom mocking code that fakes
the base class e.g. with define_method, but I’m especially asking if this
is possible with RSpec.

+1 to composition over inheritance here. Mocking at different layers
of an inheritance hierarchy sounds like trouble and screams to pull
that thing apart. Composition will allow you to separate an
inheritance hierarchy (if you do need one) from the swig base classes,
and it provides a clean separation between the swig stuff and your
application stuff.

def do_something(arg)

end

it ‘should do something with the base class when doing something’ do

No idea, how to mock Base.do_something() here

end
end


rspec-users mailing list
removed_email_address@domain.invalid
http://rubyforge.org/mailman/listinfo/rspec-users


Zach D.
http://www.continuousthinking.com
http://www.mutuallyhuman.com


#3

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


#4

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

On Thu, Mar 19, 2009 at 5:35 PM, Jeroen van Dijk
<removed_email_address@domain.invalid


#5

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


#6

2009/3/20 Jeroen van Dijk removed_email_address@domain.invalid:

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

class Base
@dummy = DummyUser
Any suggestions on what I’m doing wrong or a different approach?

+1 to composition over inheritance here. Mocking at different layers
tree
attr_accessor :base

Any base class (Swig classes in my case) should then be declared for
d = Derived.new(‘something’)
removed_email_address@domain.invalid
http://rubyforge.org/mailman/listinfo/rspec-users


rspec-users mailing list
removed_email_address@domain.invalid
http://rubyforge.org/mailman/listinfo/rspec-users


Zach D.
http://www.continuousthinking.com
http://www.mutuallyhuman.com


#7
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 :slight_smile: