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

Overriding protected abstract method in NSubstitute

我想使用 NSubstitute 创建一个 spy,但在谷歌搜索和阅读文档后无法找到具体方法。

我的想法是用一个 overriden 方法来替代,我可以像往常一样使用模拟来监视收到的呼叫等。但是如何使用 NSubstitute 实现这一点?

我不想放宽 protectedabstract 修饰符。

这是我想做的事情:

[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();
}

NSubstitute API 依赖于调用一个方法来配置它 Returns 或检查 Received() 调用,这意味着对于标准使用我们只能使用可以调用的调用通过 public 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);
    }
}

其他选项包括创建派生的 class(正如@Nkosi 指出的那样),或者使成员 internal 而不是 protected 并使用 InternalsVisibleTo 来创建您的测试代码可访问的成员(如果您执行后者,我建议安装 NSubstitute.Analyzer 以帮助确保正确配置)。