我有一种使用实体框架调用存储proc的方法,存储的proc返回了一些数据。
类似以下的东西
public async Task<IActionResult> Get(int customerId)
{
List<Product> products = _context.Products.FromSql("EXECUTE dbo.SpGatherProductInfo {0}", customerId)
.ToList();
if (!products.Any())
{
return NotFound();
}
return Ok(products);
}
如果这是对表的简单查询,我会创建一个内存DB,添加一些假条目,一切都很好。
但这使用了存储的Proc,我如何单元测试?
您过多地关注实施问题。在这种情况下,实体框架是一个实施问题。
这看起来像是将关注点封装到抽象中的好情况。
public interface IProductService {
Task<List<Product>> GatherProductInfo(int customerId);
}
并将其注入控制器
public class ProductsController : Controller {
private readonly IProductService service;
public ProductsController(IProductService service) {
this.service = service;
}
public async Task<IActionResult> Get(int customerId) {
List<Product> products = await service.GatherProductInfo(customerId);
if (!products.Any()) {
return NotFound();
}
return Ok(products);
}
}
IProductService
实现将取决于上下文和实际存储过程执行,而控制器仅取决于抽象。控制器不应关心数据的来源。
现在,这允许控制器在隔离中测试 ,而无需紧密耦合到实现问题类似于实体框架。
public async Task Product_Not_Found() {
//Arrange
var customerId = 1;
var products = new List<Product>();// Currently empty but could easily
// be populated for another test.
var mock = new Mock<IProductService>();
mock.Setup(_ => _.GatherProductInfo(customerId)).Returns(products);
var controller = new ProductsController(mock.Object);
//Act
var result = await controller.Get(customerId);
//Assert
result.Should().NotBeNull()
.And.BeTypeOf<NotFoundResult>();
}
我的一位同事弄清楚了如何模拟存储的Proc调用。
然后我只在测试中使用该模拟。
我在这里写了一篇详细的博客文章,有很多代码,所以我不仅复制博客中的所有内容 -
https://nodogmablog.bryanhogan.net/2017/11/unit-testing-entity-framework-core-core-stored-procedures/