Stubbing and asserting on new members

7 views
Skip to first unread message

David Tchepak

unread,
Jul 26, 2010, 8:43:22 PM7/26/10
to Rhino. Mocks
Just found some behaviour with Rhino Mocks' (versions 3.6 and 3.5) handling of new/hidden members, and wanted to check if it was by design or a bug.

public interface A {
    void Foo();
}

public interface B : A {
    new void Foo();
}

[Test]
public void Call_Foo_via_B_interface() {
    B b = MockRepository.GenerateStub<B>();
    A a = (A)b;

    b.Foo();

    //B.Foo() called
    b.AssertWasCalled(x => x.Foo());

    //A.Foo() called???
    b.AssertWasCalled(x => ((A)x).Foo());
    a.AssertWasCalled(x => x.Foo()); 
}

This test passes, even though I believe A.Foo() was never called (it should be hidden by B.Foo()). There is a similar behaviour for stubbing new/hidden members.

This behaviour makes things a bit easier when stubbing out a call if we want to apply it to both the hidden and actual method, so I am not sure if this is by design.

Regards,
David

Patrick Steele

unread,
Jul 27, 2010, 3:51:12 PM7/27/10
to rhino...@googlegroups.com
Hmmm.... Since "a" and "b" are pointing to the same object, I think
the test passes as it should since you called the "foo" method on the
only object that was stubbed.

How do you see this working in a real-world scenario where there's
actually a class that implements "A" and "B"?

---
Patrick Steele
http://weblogs.asp.net/psteele

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

David Tchepak

unread,
Jul 27, 2010, 8:42:24 PM7/27/10
to rhino...@googlegroups.com
Hi Patrick,

"a" and "b" point to the same object, but there are different methods being called because of method hiding. I'm not sure if the test should pass because it may be important to distinguish between the methods.

How do you see this working in a real-world scenario where there's  
actually a class that implements "A" and "B"?

Let me clarify that in a real-world scenario there should never be code that actually does this. :) However apparently there are libraries around that do. Here is an example that uses a class that implements B (and therefore implements A too). I've slightly modified A and B to make it clearer what's happening:

public interface A { string GetStr(); } 
public interface B : A { new string GetStr(); }
public class AB : B {
    string A.GetStr() { return "A"; }
    string B.GetStr() { return "B"; }
}

[Test]
public void ClassInheritanceHidingStuff() {
    var b = (B)new AB();
    Assert.That( b.GetStr(), Is.EqualTo("B") );
    Assert.That( ((A) b).GetStr(), Is.EqualTo("A") );
}

This passes. So calling via the B interface calls B.GetStr(), whereas calling via the A interface calls A.GetStr().

Now let's try and write a test for a class that uses interface B:

public class UseB {
    B _b;
    public UseB(B b) { _b = b; }
    public string GetStringFromA() { return ((A) _b).GetStr(); }
}

[Test]
public void TestUseB()
{
    var b = MockRepository.GenerateMock<B>();
    var sut = new UseB(b);
    sut.GetStringFromA();
    b.AssertWasNotCalled(x => x.GetStr()); //FAILS
}

Here we make sure UseB.GetStringFromA() calls A.GetStr(), and our test tries to assert that B.GetStr() was not called. This fails, because it seems Rhino Mocks is not differentiating between A.GetStr() and B.GetStr(). You can change the test to assert both A.GetStr() and B.GetStr() were called and the test passes, even though only one of the methods was called. So the following test passes:

[Test]
public void TestUseB() {
    var b = MockRepository.GenerateMock<B>();
    var sut = new UseB(b);
    sut.GetStringFromA();
    b.AssertWasCalled(x => x.GetStr());
    ((A)b).AssertWasCalled(x => x.GetStr());
    b.AssertWasCalled(x => ((A)x).GetStr());
    b.AssertWasCalled(x => ((B)x).GetStr());
}

I know it's the edge of an edge case, but thought I'd check and see if it were by design or a bug.
Reply all
Reply to author
Forward
0 new messages