为什么 NSubstitute.Return 没有被调用?

Why isn't NSubstitute.Return called?

我在集成测试中使用 NSubstitute 通过用模拟包装真实的实现,如下所示:

var realRepository = container.Get<IRepository>();
var proxyRepository = Substitute.For<IRepository>();

proxyRepository
    .When(repo => repo.InsertValueForEntity(Arg.Any<int>(), Arg.Any<ValueForEntity>())
    .Do(callInfo => realRepository
        .InsertValueForEntity((int)callInfo.Args()[0], (ValueForEntity)callInfo.Args()[1]));

proxyRepository
    .GetValueForEntity(Arg.Any<int>())
    .Returns(callInfo => realRepository
        .GetValueForEntity((int)callInfo.Args()[0]));

// assume these parameters are defined elsewhere
var factory = new EntityFactory(mock1, realDependency, mock2, proxyRepository);
var entity = factory.CreateEntity(/* args */); // <-- this is where proxyRepository.GetValueForEntity() should be called

proxyRepository.Received().InsertValueForEntity(entity.Id, dummyValue);
proxyRepository.Received().GetValueForEntity(Arg.Is(entity.Id));
Assert.That(entity.Value, Is.EqualTo(dummyValue));

令我感到奇怪的是,我有另一个测试使用这样的 .When().Do() 技术,它工作得很好。事实上,InsertValueForEntity 的配置似乎也适用于此。但是,GetValueForEntity 的配置 没有 工作,我不明白为什么。我在 lambda 中放置了一个断点,但它从未命中。

我在这里遗漏了关于替代品的一些棘手问题吗?

示例代码似乎没有任何明显的问题,所以我猜测这是一些未显示的代码的问题。是否可以 post 一个说明问题的可运行版本?我还建议将 NSubstitute.Analyzers 添加到您的项目中,因为它可以帮助检测有时会导致混淆测试行为的问题。

这是一个简化版本,演示了一切正常工作。如果可以修改它以重现问题,那将非常有帮助!

首先,一些支持类型:

public interface IRepository {
    ValueForEntity GetValueForEntity(int v);
    void InsertValueForEntity(int v, ValueForEntity valueForEntity);
}

public class RealRepository : IRepository {
    private readonly IDictionary<int, ValueForEntity> data = new Dictionary<int, ValueForEntity>();
    public ValueForEntity GetValueForEntity(int v) => data[v];
    public void InsertValueForEntity(int v, ValueForEntity valueForEntity) => data[v] = valueForEntity;           
}

public class ValueForEntity {
    public int Id { get; set; }
}

然后粗略估计被测对象:

public class EntityFactory {
    private readonly IRepository repo;
    public EntityFactory(IRepository repo) => this.repo = repo;
    public ValueForEntity CreateEntity(int id) {
        repo.InsertValueForEntity(id, new ValueForEntity { Id = id });
        return repo.GetValueForEntity(id);
    }
}

最后,这是 posted 测试的通过版本(我手边有 XUnit 而不是 NUnit,因此相应地更改了断言和测试属性):

[Fact]
public void Example() {
    var realRepository = new RealRepository();
    var proxyRepository = Substitute.For<IRepository>();

    proxyRepository
        .When(repo => repo.InsertValueForEntity(Arg.Any<int>(), Arg.Any<ValueForEntity>()))
        .Do(callInfo => realRepository
            .InsertValueForEntity((int)callInfo.Args()[0], (ValueForEntity)callInfo.Args()[1]));

    proxyRepository
        .GetValueForEntity(Arg.Any<int>())
        .Returns(callInfo => realRepository
            .GetValueForEntity((int)callInfo.Args()[0]));

    var factory = new EntityFactory(proxyRepository);
    var entity = factory.CreateEntity(42 /* args */); 

    proxyRepository.Received().InsertValueForEntity(entity.Id, Arg.Any<ValueForEntity>());
    proxyRepository.Received().GetValueForEntity(Arg.Is(entity.Id));
    Assert.Equal(42, entity.Id);
}

我知道类型不完全匹配,但希望您可以使用这个工作示例找出导致您的夹具出现问题的主要区别。

顺便说一句,如果您可以只使用 realRepository,是否值得在这里使用替代品?