Nsubstitute always returns null when argument is created inside the method that are being tested

Nsubstitute always returns null when argument is created inside method that is being tested

假设我们有一个注入了工作单元的服务

public class Service: IService
{ 
    IUnitOfWork unitOfWork;

   public Service(IUnitOfWork unitOfWork)
   {
       this.unitOfWork = unitOfWork;
   }

   public Item getResult(int id)
   {
       var parameter = new Parameter(id);        
      return _unitOfWork.Repository.GetItem(parameter);        

   }
}

还有这样的服务

public class Service: IService
{ 
    IUnitOfWork unitOfWork;

   public Service(IUnitOfWork unitOfWork)
   {
       this.unitOfWork = unitOfWork;
   }

   public Item getResult(Parameter parameter)
   {       
      return _unitOfWork.Repository.GetItem(parameter);        
   }
}

我想通过使用 NUnit 和 NSubstitute 的测试来测试这两种服务,如下所示:

      public void Test()
      {
        Item item= new Item();
        item.Id = 22;

        Param param = new param(item.Id);
        IRepository repository = Substitute.For<IRepository>();
        repository.GetItem(param).Returns(item);

        IUnitOfWork unitOfWork = Substitute.For<UnitOfWork>(repository);

        IService sut= new Service(unitOfWork);


       // var result = service.GetResult(id) //Example for first service, returns null
        var result = service.GetResult(param)//Example for second service, returns item

        Assert.That(result.Id == item.Id);
      }

我想知道为什么第一个服务 returns 测试时为空而第二个服务 returns 正确结果?

第二个有效,因为设置测试时使用的实例与执行测试时使用的实例完全相同,而第一个失败,因为被测方法使用它自己的实例。

您在此处传递了对 param 的引用

repository.GetItem(param).Returns(item); 

在测试设置中,失败的主题服务使用它自己的对象。

public Item getResult(int id) {
    var parameter = new Parameter(id);        
    return _unitOfWork.Repository.GetItem(parameter);
}

你基本上是在告诉模拟 "when repository.GetItem is given that specific object, return item" 但在主题中它并没有这样做,所以模拟不会像预期的那样表现。

如果您希望测试适用于这两种情况,则需要放宽参数匹配。

您可以使用 Arg.Any<T>

忽略传递的参数
public void Test() {
    var id = 22
    Item item = new Item();
    item.Id = id;

    var parameter = new Parameter(item.Id);

    IUnitOfWork unitOfWork = Substitute.For<IUnitOfWork>();
    //simplified to recursive mock
    unitOfWork.Repositoty.GetItem(Arg.Any<Parameter>()).Returns(item);

    IService service1 = new Service1(unitOfWork);
    IService service2 = new Service2(unitOfWork);

    //Act
    var result1 = service1.GetResult(id);
    var result2 = service2.GetResult(parameter);    

    //Assert
    Assert.That(result1.Id == item.Id);
    Assert.That(result2.Id == item.Id);
}

或使用 Arg.Is<T>(Predicate<T> condition).

有条件地匹配参数

我们假设 Parameter 通过构造函数设置了 object Value 属性。

//...

var parameter = new Parameter(item.Id);

IUnitOfWork unitOfWork = Substitute.For<IUnitOfWork>();
//simplified to recursive mock
unitOfWork.Repositoty.GetItem(Arg.Is<Parameter>(p => p.Value == parameter.Value))
    .Returns(item);

//...

引用NSubstitute: Argument matchers