Mocking the virtual implementation of a base class method

420 views
Skip to first unread message

twistedstream

unread,
Mar 7, 2008, 12:07:35 PM3/7/08
to Rhino.Mocks
A common OO scenario is when you have a subclass that overrides a
method and then calls the base class's implementation of that method.

For example:

public abstract class Foo
{
public virtual void Bar()
{
Console.WriteLine("Foo.Bar");
}
}

public class ConcreteFoo: Foo
{
public override void Bar()
{
Console.WriteLine("ConcreteFoo.Bar");
base.Bar();
}
}

If ConcreteFoo was created and its Bar method was called, the Console
output would look like:

ConcreteFoo.Bar
Foo.Bar

Let's say I'm testing the ConcreteFoo class's Bar method, but I want
to mock the base class's Foo.Bar method so it doesn't actually
execute; I just want to set an expectation that it gets called.

If I write my test like this:

[TestMethod]
public void TestMethod1()
{
ConcreteFoo target;
MockRepository mocks = new MockRepository();
using (mocks.Record())
{
target = mocks.PartialMock<ConcreteFoo>();
target.Bar();
}
using (mocks.Playback())
{
target.Bar();
}
}

Neither ConcreteFoo.Bar or Foo.Bar get called since the mock object
class overrides them both. I need a way to allow ConcreteFoo.Bar to
execute, but mock Foo.Bar.

Thanks!
~pete

Ayende Rahien

unread,
Mar 7, 2008, 12:12:44 PM3/7/08
to Rhino...@googlegroups.com
There is not way of doing this, I am afraid.
The problem is that from the point of view of the runtime, there is only one such method, not two.
Only the direct subclass can call the parent method.

dalesmithtx

unread,
Apr 22, 2008, 4:56:30 PM4/22/08
to Rhino.Mocks
I'm running into a similar scenario. What is a good testing approach
for a scenario like this?
> > ~pete- Hide quoted text -
>
> - Show quoted text -

Steve Freeman

unread,
Apr 23, 2008, 6:32:24 AM4/23/08
to Rhino...@googlegroups.com
It sounds like the difficulties you're having in testing are telling
you that something needs to be broken out, that you should be using
more composition in your design. If, for example, you were calling a
helper object, rather than a static Console method, then you could
stub that rather than the internals of your object.

S.

Steve Freeman
http://www.mockobjects.com

Winner of the Agile Alliance Gordon Pask award 2006

dalesmithtx

unread,
Apr 23, 2008, 10:40:37 AM4/23/08
to Rhino.Mocks
@Steve - Thanks, Steve. Admittedly, I am somewhat new to using
Rhino.Mocks, but I do understand that designing for testability will
drive you towards certain design decisions. I hear what you're
saying, but to me that still seems unsatisfying.

Let me give you an example more like what I'm facing. Unlike the
example above, I'm not making calls to static methods anywhere in this
class structure. I've just got an abstract class and a concrete
subclass, just one layer of inheritance, like so:

public abstract class Foo
{
public virtual int Bar(int myvalue)
{
return myvalue + 1;
}
}


public class ConcreteFoo: Foo
{
public override int Bar(int myvalue)
{
myvalue += base.Bar(myvalue);
return myvalue + 5;
}
}

In this example, what I think I want to test is that ConcreteFoo.Bar()
increases the value by 5. It seems to me that I want to able to
ignore the implementation details of Foo.Bar() so that I can
adequately test ConcreteFoo.Bar() in isolation. So 1) is that really
what I want to test here, and 2) if so, how do I do it? Seems to me
that Ayende is right - I won't be able to. If that's not really what
I should be testing here, then what is?

Thanks for your help!
> Steve Freemanhttp://www.mockobjects.com
>
> Winner of the Agile Alliance Gordon Pask award 2006- Hide quoted text -

isaiah perumalla

unread,
Apr 23, 2008, 10:59:04 AM4/23/08
to Rhino...@googlegroups.com
hey i would suggest not to use any mocking in this case since not colloborating with any other object and with Inheritance there is always tight coupling between the super class and sub class . I would suggest to just do a state-based test on the concreteFoo class Bar method and assert it gives you the expected value for a given input.

Isaiah
--
Isaiah Perumalla

Richard...@quixotecorp.com

unread,
Apr 23, 2008, 2:28:48 PM4/23/08
to Rhino...@googlegroups.com
Dale,

I would echo what Isaiah said... in your specific example, because of
the tight coupling between the two it is probably not appropriate to
even attempt to use mock objects.

However... one of the things I've found helpful over the years is the
fact that it is often appropriate to "favor encapsulation over
inheritance". Clearly your example isn't telling us everything (and
there are probably excellent reasons for using inheritance), so what I'm
about to suggest may not be appropriate. Use at your own risk!

What you appear to be doing here is requiring that a certain method
(Bar) is provided, while also providing a default implementation. What
you could do it create an interface (IFoo) which both Foo and
ConcreteFoo implement.

In ConcreteFoo's default constructor, instantiate a (now non abstract,
but probably not available for public creation) Foo object. In your
implementation of Bar, call that object's Bar method before performing
your own operation.

Create another mechanism (typically it's an alternate constructor, often
with internal-only visibility) which allows you to specify the
underlying IFoo object.

Now you have done so, you can use RhinoMocks to create a mock "Foo"
(actually an IFoo) which returns the specific values you need. That way
you can verify the operation of ConcreteFoo's Bar operation, regardless
of what Foo itself will return.

Does this make sense, or am I a babbling idiot?

Regards,
Richard

Thanks for your help!

[Older message history trimmed]


* C O N F I D E N T I A L I T Y N O T I C E *
-----------------------------------------------------------
The content of this e-mail is intended solely for the use of the individual or entity to whom it is addressed. If you have received this communication in error, be aware that forwarding it, copying it, or in any way disclosing its content to any other person, is strictly prohibited. Quixote Traffic Corporation is neither liable for the contents, nor for the proper, complete and timely transmission of (the information contained in) this communication. If you have received this communication in error, please notify the author by replying to this e-mail immediately and delete the material from any computer.

dalesmithtx

unread,
Apr 23, 2008, 3:07:00 PM4/23/08
to Rhino.Mocks
@Richard - You're not babbling at all, Richard. Both your and
Isaiah's answers make a lot of sense, and each is appropriate for
different design problems. I'm going to percolate on them both for a
bit, and then blog about them here:

http://creedcultcode.blogspot.com

Thanks so much for your help,
Dale
> The content of this e-mail is intended solely for the use of the individual or entity to whom it is addressed. If you have received this communication in error, be aware that forwarding it, copying it, or in any way disclosing its content to any other person, is strictly prohibited. Quixote Traffic Corporation is neither liable for the contents, nor for the proper, complete and timely transmission of (the information contained in) this communication. If you have received this communication in error, please notify the author by replying to this e-mail immediately and delete the material from any computer.- Hide quoted text -

Steve Freeman

unread,
Apr 23, 2008, 4:09:04 PM4/23/08
to Rhino...@googlegroups.com
The others have come up with good responses. I'd like to make one more
point.

The common question I ask myself is, "If this thing worked, how would
I know"? What are the implications of the addition by 5 that are
visible outside the object? It might be the contents of a call to a
neighbouring object, or the value of a property. That's what you
should be testing.

As the others said, mocking within an object ties the test to the
object's implementation not to its behaviour. Use sparingly.

S.

Steve Freeman

dalesmithtx

unread,
Apr 24, 2008, 10:12:47 AM4/24/08
to Rhino.Mocks
"If this thing worked, how would I know"? - Great point, Steve.

Thanks for your help,
Dale
Reply all
Reply to author
Forward
0 new messages