Search code examples
c#unit-testingmoqpartial-mocks

Partial mocking a class internal method with Moq


I have a class that contains a public method, that relies on a internal method in order to properly return its value.

Let's consider the following class and test file:

public class ClassUnderTest
{
    public string NotMockedPublicMethod()
    {
        return MockedMethod();
    }

    virtual public string MockedMethod()
    {
        return "original";
    }
}

The following test case would work:

var mock = new Mock<ClassUnderTest> { CallBase = true };
mock.Setup(m => m.MockedMethod()).Returns("mocked");

Assert.AreEqual("mocked", mock.Object.NotMockedPublicMethod());

But let's say this MockedMethod() of mine has no utility externally. The problem is that marking this method as internal (even using InternalsVisibleTo() properly):

virtual internal string MockedMethod()

will make the exactly same test fails with the message Assert.AreEqual failed. Expected:<mocked>. Actual:<original>.

Is this a Moq bug or some limitation?


Solution

  • It is not a bug or limitation. Your test is failing after making the method internal (even after adding InternalsVisibleTo) because it is not calling the mocked method but is calling the actual method.

    You need to add InternalsVisibleTo for DynamicProxyGenAssembly2 as well as per the below Url.

    [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]

    http://www.blackwasp.co.uk/MoqInternals.aspx

    The Url doesn't provide proper explanation but here it is:

    Moq uses Castle Project's DynamicProxy to generate proxies on the fly at run-time so that members of an object can be intercepted without modifying the code of the class. That's how Moq returns a value specified in "Setup().Returns" (String "mocked" in your case)

    Dynamic Proxy Url: http://www.castleproject.org/projects/dynamicproxy/

    I looked at the source code (see url below) for DynamicProxy and I see that it uses "DynamicProxyGenAssembly2" as the assembly name for the assembly generated and that is why you need to add InternalsVisibleTo for DynamicProxyGenAssembly2 as well.

    public static readonly String DEFAULT_ASSEMBLY_NAME = "DynamicProxyGenAssembly2";
    

    https://github.com/castleproject/Castle.DynamicProxy-READONLY/blob/ed8663b23a54bed641e5f97e39a6bc16fe0d976f/src/Castle.DynamicProxy/ModuleScope.cs