简体   繁体   中英

Protected virtual method of a mocked abstract class is not called

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.

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 (ie 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.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM