views:

92

answers:

2

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>();
}
+2  A: 

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
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 Spinelli
A: 

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>();
        }
    }
}
adrift
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 Spinelli
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.
adrift
@adrift: 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 Spinelli
@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.
adrift
I'm using 3.1.416.3. Isn't 4.0 beta? I'll give that a try though if nothing else works.
Matt Spinelli