Want to show your appreciation? Please to my charity.

Thursday, April 16, 2009

Rhino Mocks Strikes to Mock Non-Virtual Interface Implementation

Rhino Mocks has been my favorite mocking framework for a long time now and I'm more than a happy user. Today I got into a test scenario that seems to be straightforward but no matter how I struggle with it, Rhino Mocks refused to do its work.

That is a none virtual method on a class that I need to mock. Ok Ok, I hear you! Rhino Mocks cannot override none virtual method and declaring new is meaningless as you can never reference directly to the mock class. But the class implements an interface, "Then mock the interface!" I hear you again. Well, a) I don't want to mock every method or I would like to CallOriginalMethod; b) The class I want to mock is the class I want to test...

Totally confused? Let's jump to the code that will explain it better.

Let's say there is an interface and an implementation that I don't own

    public interface IDoNotOwn {
        int DirtyWork(int x);
        int Outer(int y);
    }

    public class DoNotOwn : IDoNotOwn {
        public int DirtyWork(int x) {
            // do something that hard for unit test to setup.
            throw new Exception("Don't call me in unit test");
        }

        public int Outer(int y) {
            return DirtyWork(y + y);
        }
    }

Unfortunately, it is out of my control that DoNotOwn implementation didn't declare the method to be virtual ("Many thanks" to Microsoft for the thoughtful default!)

I need to write my own implementation of IDoNotOwn but don't want to re-write the complex logic in the DirtyWork which is working perfectly fine. So naturally I have MyClass inherit from DoNotOwn.

    public class MyClass : DoNotOwn, IDoNotOwn {
        public new int Outer(int y) {
            return ((IDoNotOwn)this).DirtyWork(y*y);
        }
    }

So far everything looked normal and should just work fine. Let's write unit test for MyClass. I want to mock the call to the DirtyWork. Since my class implement an interface, I hope Rhino Mocks will be able to do that for me. My first attempt was easy to understand but didn't work.

    [TestFixture] public class MyClassTest {
        [Test] public void UsingRhinoMocks() {
            const int workResult = 293848;
            MockRepository mockery = new MockRepository();
            IDoNotOwn o = mockery.CreateMock<MyClass>();
            Expect.Call(o.DirtyWork(4)).Return(workResult);
            mockery.ReplayAll();
            Assert.That(o.Outer(2), Is.EqualTo(workResult));
            mockery.VerifyAll();
        }
    }

It gave me the Exception

System.Exception: Don't call me in unit test 
at MockDemo.DoNotOwn.DirtyWork(Int32 x) in MockDemoTest.cs: line 14
at MockDemo.MyClassTest.OuterMethod() in MockDemoTest.cs: line 33

Alright, so Rhino Mocks doesn't generate the method stub even if there is an interface exists. How about let me telling it explicitly? Let's give it second try by using MultiMock:

            IDoNotOwn o = mockery.CreateMultiMock<MyClass>(typeof(IDoNotOwn));

Now I got this:

Rhino.Mocks.Exceptions.ExpectationViolationException: IDoNotOwn.Outer(2); Expected #0, Actual #1. 
......
at MyClassProxybd7bb9a610da4d3fb0971368461c5b7c.Outer(Int32 y)
at MockDemo.MyClassTest.OuterMethod() in MockDemoTest.cs: line 35

Fine, I'll setup expectation and call the original method:

            IDoNotOwn o = mockery.CreateMultiMock<MyClass>(typeof(IDoNotOwn));
            Expect.Call(o.Outer(2)).CallOriginalMethod(OriginalCallOptions.NoExpectation);

Still, it throws me off

System.InvalidOperationException: Can't use CallOriginalMethod on method Outer because the method is abstract. 
at Rhino.Mocks.Impl.MethodOptions`1.AssertMethodImplementationExists()
at Rhino.Mocks.Impl.MethodOptions`1.CallOriginalMethod(OriginalCallOptions options)
at MockDemo.MyClassTest.OuterMethod() in MockDemoTest.cs: line 34

Now, I'm out of idea of using Rhino Mocks to achieve this. Anybody made this work with Rhino Mocks, please let me know.

I end up writing my own stub which worked very well for me. I believe the same kind of Stub can be easily generated by any mocking system.

        [Test] public void UsingMyStub() {
            const int workResult = 293848;
            MyClassStub stub = new MyClassStub();
            IDoNotOwn o = stub.ExpectCallDirtyWork(4).WillReturn(workResult);
            Assert.That(o.Outer(2), Is.EqualTo(workResult));
            stub.VerifyAll();
        }

        private class MyClassStub : MyClass, IDoNotOwn {
            private int _expectedI, _returnValue;
            private bool _isCalled;
            internal MyClassStub ExpectCallDirtyWork(int i) {
                _expectedI = i; return this;
            }

            internal MyClassStub WillReturn(int value) {
                _returnValue = value; return this;
            }

            internal void VerifyAll() {
                Assert.IsTrue(_isCalled, "Call to DirtyWork was not made.");
            }

            public new int DirtyWork(int i) {
                Assert.That(i, Is.EqualTo(_expectedI));
                Assert.IsFalse(_isCalled, "Duplicated call to DirtyWork.");
                _isCalled = true;
                return _returnValue;
            }
        }

Well, the example here is rather fictional but this all came out from a real world problme when writing the test cases for my OracleOdpTemplate.

Update

(4/19/2009) I asked in the RhinoMocks group mailing list. Tim Barcz reminded me about the adapter pattern. I didn't give that a consideration because, in my real world problem, I would have to wrote a hundred of methods plus their test cases if I attempt to adapt it. But after gave it another thought, I found that I can use a variation of adapter to help testing. Here is the changed the class.

        public class MyClass : DoNotOwn, IDoNotOwn {
            internal IDoNotOwn self;

            public MyClass() {
                self = this;
            }

            public new int Outer(int y) {
                return self.DirtyWork(y * y);
            }
        }

And the test case for it.

        [Test] public void UsingRhinoMocks() {
            const int workResult = 293848;
            MockRepository mockery = new MockRepository();
            var o = new MyClass {self = mockery.CreateMock<IDoNotOwn>()};
            Expect.Call(o.self.DirtyWork(4)).Return(workResult);
            mockery.ReplayAll();
            Assert.That(o.Outer(2), Is.EqualTo(workResult));
            mockery.VerifyAll();
        }

That worked fine for now but I still wish I can easily mock any interface method using RhinoMocks regardless of the virtual declaration of the implementation.

No comments:

Post a Comment