Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use moq to test code that calls protected helpers

I currently run tests that look like the following:

// In Blah.cs
public class ClassUnderTest

{

    public bool MethodUnderTest()

    {

        // Do a bunch of stuff...

        return HelperMethod();

    }



    protected virtual bool HelperMethod()

    {

        bool success = false;

        // Proprietary Hardware Access.

        // Database Calls.

        // File System Modifications.

        return success;

    }

}


// In TestBlah.cs

public class TestStub : ClassUnderTest

{

    public bool HelperMethodReturnValue;



    protected override bool HelperMethod()

    {

        return HelperMethodReturnValue;

    }

}



[TestClass]

public class TestingClass

{

    [TestMethod]

    public void ClassUnderTest_MethodUnderTest_TestHelperReturnsTrue()

    {

        var stub = new TestStub();

        stub.HelperMethodReturnValue = true;

        Assert.IsTrue(stub.MethodUnderTest());

    }



    [TestMethod]

    public void ClassUnderTest_MethodUnderTest_TestHelperReturnsFalse()

    {

        var stub = new TestStub();

        stub.HelperMethodReturnValue = false;

        Assert.IsFalse(stub.MethodUnderTest());

    }

}

The above looks fine for simple things, however the stub class gets exponentially bigger and more complex quickly. I'd like to replace the stub class using Moq. However this won't compile because for some reason I can't set a return value on a protected method.

[TestMethod]

public void ClassUnderTest_MethodUnderTest_TestHelperReturnsFalse()

{

    var mockClass = new Mock<ClassUnderTest>();
    mockClass.Protected().Setup("HelperMethod").Returns(false);

    Assert.IsFalse(mockClass.Object.MethodUnderTest());

}

Anyone know how I'd go about doing this? Can I do this with moq?

like image 216
WhiteleyJ Avatar asked Oct 21 '11 15:10

WhiteleyJ


2 Answers

Looking at the moq source code I'd guess you need to explicitly call the generic version of Setup. The non-generic version seems to be used for void methods. So try

mockClass.Protected().Setup<bool>("HelperMethod").Returns(false);

Beside this, I'd recommend re-thinking your class design. If HelperMethod() is doing such a bunch of things it would be worth its own class that is injected as a dependency into ClassUnderTest. Testing a mock object, instead of using a mock object to test something "real", is not what mocking frameworks are made for (not in the first place, at least).

like image 109
Matthias Avatar answered Nov 10 '22 02:11

Matthias


Protected methods are not a great way of isolating dependencies, but it does sometimes come up, particularly when adapting legacy code for testability. One option that avoids the awkward string-based Moq syntax is to make the method 'protected internal' (or just 'internal' if you don't intend to override it in normal usage from other assemblies.) You then use InternalsVisibleTo on the assembly to expose the method. This is a bit of a hack, but using a protected method for this purpose is already a bit of a hack. In some ways I prefer the 'internal' approach, as it makes it clear that this is a backdoor method that you're not supposed to use (except for testing), as opposed to a protected method that you might expect to override in normal usage.

like image 30
Dan Bryant Avatar answered Nov 10 '22 01:11

Dan Bryant