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 列表时没有性能差异可言,因此我建议重构您的代码。那样就更清楚了。
我正在为我的方法编写单元测试:
[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 列表时没有性能差异可言,因此我建议重构您的代码。那样就更清楚了。