0

I have code that uses MoQ to create a partial stub. I'd prefer to interact with the interface instead of the concrete implementation so that I won't have to modify the unit test if I have a different implementation of the interface.

So for example, I have a factory method such as:

private Mock<ISomeInterface> ISomeInterfaceStubFactory()
{
    return new Mock<SomeConcreteImplementation>();
}

Here is the code that calls the method:

var partialStub = ISomeInterfaceStubFactory();            
partialStub.Setup(m => m.MethodToStubOutThatMethodToTestCalls(It.IsAny<string>())).Returns(new List<SomeOtherObject>());
partialStub.CallBase = true;

var actualResult= partialStub.Object.MethodToTest();

Assert.That(actualResult, Is.EqualTo(expectedResult));

The problem is that when doing this is that ISomeInterfaceStubFactory won't compile. So I changed it to be like below, but doing this seems to break the partial stub. The actual implemented MethodToStubOutThatMethodToTestCalls operation gets called, not the stubbed version. Basically I'm trying to use polymorphism with the stub object. Is there anyway to do this? I'd like my unit test to not be highly coupled to the concrete implementation.

private Mock<ISomeInterface> ISomeInterfaceStubFactory()
{
    return new Mock<SomeConcreteImplementation>.As<ISomeInterface>();
}
Matt
  • 14,353
  • 5
  • 53
  • 65
  • There's some information at this question: http://stackoverflow.com/questions/2822947/when-mocking-a-class-with-moq-how-can-i-callbase-for-just-specific-methods - specifically that `mock.CallBase` affects the entire mock, which is what I would expect to happen. It seems like a code smell to me that a public method on your interface would call another public method on your interface; you should pull out that logic into either a private method or (better, IMO) a dependency that you can mock out. – Matt Mills Oct 11 '10 at 01:58
  • @arootbeer: Thanks for the link. That is the issue I am running into I believe. I'll have to think about a better design (if that is the only way around this issue). Also if RhinoMocks could overcome this, I could switch to that as well. The reason why I have a public method on my interface calling another public method is because one preforms a higher level task of which the other method it calls is a lower level task. The consuming code of this interface will call either method depending on the need (it may just need the result of the lower level method). – Matt Oct 11 '10 at 02:32
  • That still begs the question why they should be on the same interface (they can still reside on the same class, if you'd like, but be exposed via separate interfaces). – Matt Mills Oct 11 '10 at 02:49

2 Answers2

2

I think you are missing the point of mock objects. Returning a mock from a concrete implementation makes no sense. The idea is to have the class under test depend on some interface or abstract which you could mock.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • I'm not wanting to mock the object, I want to stub it partially. The class has it's public methods set to be virtual. This way if I want to unit test a method that calls other public methods in the class, I can stub out those other methods. This isolates only the code I want to test. – Matt Oct 08 '10 at 18:32
  • Okay, now I think I see where you are coming from and arootbeer's comment to my question. It's all about design. A better design would not put me in this situation in the first place. Thanks. – Matt Feb 14 '11 at 04:43
0

Revising my answer per your clarification. I don't disagree with arootbeer, but I do want to understand what you are doing and why it doesn't work.

Here's a simple example of what I think you are trying to do. The test passes for me for both concrete implementations. Is this what you are trying to do, and does this example work for you?

Interface and classes:

using System;

namespace ClassLibrary1
{
    public interface IFoo
    {
        string GetBaseString();
        string GetExtendedString();
    }
    public class Foo_A : IFoo
    {
        public virtual string GetBaseString()
        {
            return "Foo_A";
        }
        public virtual string GetExtendedString()
        {
            return GetBaseString() + "_Bar";
        }
    }
    public class Foo_B : IFoo
    {
        public virtual string GetBaseString()
        {
            return "Foo_B";
        }
        public virtual string GetExtendedString()
        {
            return GetBaseString() + "_Bar";
        }
    }
}

Unit test:

using System;
using Xunit;
using Moq;

namespace ClassLibrary1.UnitTests
{
    public class Class1
    {
        [Fact]
        public void GetExtendedString_ReturnsExtendedString()
        {
            var partialFoo = IFooFactory();

            partialFoo.Setup(x => x.GetBaseString()).Returns("Foo");
            partialFoo.CallBase = true;

            string result = partialFoo.Object.GetExtendedString();

            Assert.Equal("Foo_Bar", result);
        }

        private Mock<IFoo> IFooFactory()
        {
            return new Mock<Foo_A>().As<IFoo>();
            //return new Mock<Foo_B>().As<IFoo>();
        }
    }
}
Jeff Ogata
  • 56,645
  • 19
  • 114
  • 127
  • That won't work as the method to test never actually gets called. I noticed a typo in my example (I had named the method load both for the stubbed method and the method under test). I updated my example. Hopefully this is more clear. – Matt Oct 11 '10 at 01:29
  • Ok, I think I understand now. I tried to set up a simple example of what you are doing, and it worked. Will post my code so it can be verified. – Jeff Ogata Oct 11 '10 at 03:56
  • That is what I am trying to do. In my case the implementation of GetBaseString is being called, not the stub. I'll have to investigate this later as I have other work to do today. I'll also try copying your code and seeing if I can get it to work. I don't think it matters, but the code under test is VB.Net, even though I'm testing with C#. – Matt Oct 11 '10 at 12:49
  • @Matt Spinelli, if it doesn't work, you might want to get the latest version of Moq, if you don't already have it. There was an [issue](http://code.google.com/p/moq/issues/detail?id=229) reported with CallBase that was fixed recently. It's not exactly the same, but could be related. I am using version 4.0.10501.6. – Jeff Ogata Oct 11 '10 at 15:39
  • I'm using 3.1.416.3. Isn't 4.0 beta? I'll give that a try though if nothing else works. – Matt Oct 11 '10 at 16:08