0

I am mocking an abstract class with NSubstitute and expect its protected virtual method to be called.

public abstract class A 
{
    protected virtual bool ProtectedMethod()
    {
        return true;
    }
    public bool PublicMethod()
    {
        return ProtectedMethod();
    }
}

public class ATest
{
    [Fact]
    public void Test()
    {
        var anA = Substitute.For<A>();

        var result = anA.PublicMethod();

        Assert.True(result);
    }
}

This test fails when executed. In fact, it fails even if the class is not abstract. If this is normal behavior, what should I do to ensure the ProtectedMethod is called?

PS. If the method is not virtual, it works as expected.

Vlad
  • 2,475
  • 21
  • 32
  • _You need to test a system for bugs. You decide to mock it. Now you have two problems_. _[More...](https://mickyd.wordpress.com/2016/05/25/have-you-unit-tested-that-aileron-servo/)_ –  May 15 '19 at 23:23
  • NSubstitute will only work properly with interfaces or with class members that are overridable from the test assembly (public virtual, protected virtual, protected internal virtual, or internal virtual with InternalsVisibleTo attribute applied). Be careful substituting for classes with non-virtual members. See Creating a substitute and How NSubstitute works for more information. https://nsubstitute.github.io/help/creating-a-substitute/#substituting_infrequently_and_carefully_for_classes – Mate May 15 '19 at 23:26
  • Ex: if you change public bool PublicMethod() to virtual , you'll can run anA.PublicVIRTUALMethod().Returns(false); – Mate May 15 '19 at 23:32
  • If the protected method returned false and you were testing for that, the test would pass even though the method isn't called. Based on that I'd only use a mock to replace the behavior of an entire dependency when testing a class that uses it. To test an abstract class I'd create an inherited class and test that without a mock. Otherwise it's not easy to tell what's actually getting tested. – Scott Hannen May 15 '19 at 23:53
  • Mocks are not created for the types you want to unit test. On your test you are testing the mock not the actual class. – Chetan May 16 '19 at 00:20

1 Answers1

0

As pointed out in the comments, be careful substituting for classes. I recommend installing the NSubstitute.Analyzers to pick up issues with class substitutes at compile time.

The reason this test is failing is because you are substituting for A, so NSubstitute replaces all virtual implementations with substitute ones (which generally return default unless otherwise stubbed out, in this case false).

You can use a partial substitute which will maintain the existing implementation by default (i.e. ProtectedMethod will keep returning true as per the base implementation):

[Fact]
public void TestUsingPartialSub() {
    var anA = Substitute.ForPartsOf<A>();

    var result = anA.PublicMethod();

    Assert.True(result);
}

"... what should I do to ensure the ProtectedMethod is called?"

NSubstitute can not assert on protected methods (it works via the publicly accessible API). If possible, you can refactor the code to use a strategy pattern to inject the protected behaviour. This will make the code more flexible (including the flexibility to inject different behaviour for testing), at the cost of a slightly more complex design.

public interface IProtectedMethod {
    bool ProtectedMethod();
}

public class AA {
    private readonly IProtectedMethod x;
    public AA(IProtectedMethod x) {
        this.x = x;
    }
    public bool PublicMethod() {
        return x.ProtectedMethod();
    }
}

public class AATest {
    [Fact]
    public void TestUsingStrategySub() {
        var x = Substitute.For<IProtectedMethod>();
        var anA = new AA(x);

        anA.PublicMethod();

        x.Received().ProtectedMethod();                
    }
}

(Please excuse the naming in this example, I've tried to keep it similar to the original to make it clearer where the various bits of logic have moved.)

David Tchepak
  • 9,826
  • 2
  • 56
  • 68