为什么不调用NSubstitute.Return?

时间:2019-10-18 22:33:05

标签: c# .net nsubstitute

我在集成测试中使用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上设置了一个断点,但它从未命中。

我在这里缺少关于替补的一些棘手的东西吗?

1 个答案:

答案 0 :(得分:1)

示例代码似乎没有任何明显的问题,因此我猜测这是一些未显示的代码的问题。是否可以发布说明问题的可运行版本?我还建议将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);
    }
}

最后,这是发布的测试的通过版本(我拥有XUnit而不是NUnit,因此相应地更改了assert和test属性):

[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,是否值得在这里使用替代品?