在NSubstitute中覆盖受保护的抽象方法

时间:2019-04-01 12:54:43

标签: unit-testing mocking nsubstitute spy

我想使用NSubstitute创建一个间谍,但是在经过大量的搜索和阅读文档后却找不到答案。

这个想法是用一个 overriden 方法代替,我可以用它来监视接收到的呼叫,等等。但是,如何使用NSubstitute实现此目标?

我不希望既不放松protected也不放松abstract修饰符。

这就是我想要做的:

[Test]
public void Items_Add_shouldCall_ItemAddedMethod()
{
    var sut = Substitute.For<ItemsRepository>();
    // how to configure???
    sut.Items.Add(Substitute.For<IItem>());

    sut.Received().ItemAdded();  // currently cannot do this

// ...

public abstract class ItemsRepository
{
    public ObservableCollection<IItem> Items { get; set; } 
        = new ObservableCollection<IItem>();

    // ...

    protected abstract void ItemAdded();
}

1 个答案:

答案 0 :(得分:2)

NSubstitute API依靠调用一种方法来配置其Returns或检查Received()调用,这意味着对于标准用途,我们只能处理可以通过公共API调用的调用(不包括protectedprivate成员)。问题的abstract部分没有问题;最好将NSubstitute与可访问的abstract成员一起使用。

尽管C#设计使我们无法直接与protected成员进行交互,但NSubstitute仍会记录对这些成员的调用。我们可以使用"unofficial" .ReceivedCalls()扩展名来检查这些内容:

public abstract class ItemsRepository {
    public virtual void Add(int i) { ItemAdded(); }
    protected abstract void ItemAdded();
}

public class Fixture {
    [Fact]
    public void CheckProtectedCall() {
        var sub = Substitute.For<ItemsRepository>();
        sub.When(x => x.Add(Arg.Any<int>())).CallBase();

        sub.Add(42);

        var called = sub.ReceivedCalls().Select(x => x.GetMethodInfo().Name);

        Assert.Contains("ItemAdded", called);
    }
}

其他选项包括创建派生类(如@Nkosi所指出的),或使成员internal代替protected,并使用InternalsVisibleTo使成员可被您的测试代码访问(如果您选择后者,则建议安装NSubstitute.Analyzer,以帮助确保正确配置)。