return 模拟对象到测试方法的正确方法

Right way to return mocked object to tested method

我正在为我的方法编写单元测试:

[HttpGet("sani")]
[RequireHttps]
public async Task<IActionResult> GetSuppliersAsync(double latitude, double longitude)
{
    if (ModelState.IsValid)
    {
        var supplierList = new List<Supplier>();
        var externalSupplierList = new List<ExternalSupplier>();
        try
        {
            await _supplierRepository.GetSuppliersAsync(supplierList);
            await _supplierRepository.GetExternalSupplierAsync(externalSupplierList);
        }
        catch (Exception)
        {
            return BadRequest("db");
        }

        var coll = new Dictionary<dynamic, double>();

        AddToCollectionInParallel(supplierList, latitude, longitude, coll);

        coll.OrderBy(x => x.Value);
        AddExternalToCollectionInParallel(externalSupplierList, latitude, longitude, coll);

        return new OkObjectResult(coll.Keys);
    }
    return BadRequest("model");
}

如您所见,我传递给 GetSuppliersAsync()GetExternalSuppliers() 列表,应该在那里填写。我没有 return 列表(所以它会像:var supplierList = _supplierRepository.GetSuppliersAsync();)因为传递对象(列表)并将其填充到方法中然后 return 列表会更快。 所以现在我想写一个单元测试。我正在使用 Xunit 进行单元测试。我的测试方法:

[Xunit.Fact]
public async Task GetSuppliersAsyncDbTest()
{
    var list = new List<Supplier>();
    list.Add(new Supplier { SupplierId = "ertre", SupplierName = "test" });
    var extList = new List<ExternalSupplier>();
    extList.Add(new Models.ExternalSupplier { Id = 4});
    _mockRepo.Setup(repo => repo.GetSuppliersAsync(list)).Returns(Task.FromResult(list));
    _mockRepo.Setup(repo => repo.GetExternalSuppliersAsync(extList)).Returns(Task.FromResult(extList));
    var controller = new SupplierController(_mockRepo.Object);
    var result = await controller.GetSuppliersAsync(0.0, 0.0);
    var viewResult = Xunit.Assert.IsType<OkObjectResult>(result);
    Xunit.Assert.Equal(200, viewResult.StatusCode);
}

我的问题是,在单元测试期间,当我调用第 await _supplierRepository.GetSuppliersAsync(supplierList); 行时,它 return 是一个空列表,尽管我已经模拟到 return 列表,里面有一个项目.但是当我更改为 var supplierList = _supplierRepository.GetSuppliersAsync(); 时,它 return 是一项,正如预期的那样。 任何想法如何解决它,或者我应该将方法重写为 return values as List?

您使用供应商列表的具体实例模拟了存储库的 GetSuppliersAsync()。但是,在您的控制器方法中使用了另一个实例并且未返回您的存根值。要解决此问题,您应该在模拟设置期间使用参数匹配器:

_mockRepo.Setup(repo => repo.GetSuppliersAsync(It.IsAny<List<Supplier>>()))
.Returns(Task.FromResult(list));

您的存储库 GetSuppliersAsync() 是如何工作的?它会更改传入参数的列表还是 returns 供应商列表?如果是第二种情况,那么您应该分配从存储库返回的列表:

supplierList = await _supplierRepository.GetSuppliersAsync(supplierList);
externalSupplierList = await _supplierRepository.GetExternalSupplierAsync(externalSupplierList);

两种方式都可以:

到return通过参数:

 var supplierList = new List<Supplier>();
 await _supplierRepository.GetSuppliersAsync(supplierList);

使用

_mockRepo.Setup(repo => repo.GetSuppliersAsync(It.IsAny<List<Supplier>>()))
.Returns((List<Supplier> x) => {x.AddRange(list); return Task.Delay(0); });

或将您的代码重构为 return 列表:

 var supplierList =  await _supplierRepository.GetSuppliersAsync();

使用

_mockRepo.Setup(repo => repo.GetSuppliersAsync())
.Returns(Task.FromResult(list));

在 returning 列表时没有性能差异可言,因此我建议重构您的代码。那样就更清楚了。