ASP.Net Core单元测试异步控制器
本文关键字:异步控制 控制器 异步 单元测试 Net Core ASP | 更新日期: 2023-09-27 18:16:57
我有这样一个测试:
[Fact]
public async void Can_Paginate()
{
//fake data
var product1 = new Product { ProductId = 1, ProductName = "P1" };
var product2 = new Product { ProductId = 2, ProductName = "Product 2" };
var product3 = new Product { ProductId = 3, ProductName = "Product 3" };
var product4 = new Product { ProductId = 4, ProductName = "Product 4" };
var product5 = new Product { ProductId = 5, ProductName = "Product 5" };
var data = new List<Product>
{
product1,
product2,
product3,
product4,
product5
}.ToAsyncEnumerable();
var category1 = new Category { CategoryId = 1 };
var prodCategoryData = new List<ProductCategory>
{
new ProductCategory { Product =product1,Category = category1},
new ProductCategory { Product =product2,Category = category1},
new ProductCategory { Product =product3,Category = category1},
new ProductCategory { Product =product4,Category = category1},
new ProductCategory { Product =product5,Category = category1}
}.ToAsyncEnumerable();
var dbSetMock = new Mock<DbSet<Product>>();
//dbSetMock.As<IQueryable<Product>>().Setup(m => m.Provider).Returns(data.Provider);
//dbSetMock.As<IQueryable<Product>>().Setup(m => m.Expression).Returns(data.Expression);
//dbSetMock.As<IQueryable<Product>>().Setup(m => m.ElementType).Returns(data.ElementType);
//dbSetMock.As<IQueryable<Product>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var pcDbSetMock = new Mock<DbSet<ProductCategory>>();
// pcDbSetMock.As<IQueryable<ProductCategory>>().Setup(m => m.Provider).Returns(prodCategoryData.Provider);
//pcDbSetMock.As<IQueryable<ProductCategory>>().Setup(m => m.Expression).Returns(prodCategoryData.Expression);
//pcDbSetMock.As<IQueryable<ProductCategory>>().Setup(m => m.ElementType).Returns(prodCategoryData.ElementType);
//pcDbSetMock.As<IQueryable<ProductCategory>>().Setup(m => m.GetEnumerator()).Returns(prodCategoryData.GetEnumerator());
Mock<ApplicationDbContext> customDbContextMock = new Mock<ApplicationDbContext>();
customDbContextMock.Setup(x => x.Products).Returns(dbSetMock.Object);
customDbContextMock.Setup(x => x.ProductCategories).Returns(pcDbSetMock.Object);
var loggerMock = new Mock<ILogger<ProductsController>>();
var envMock = new Mock<IHostingEnvironment>();
var httpContext = new Mock<HttpContext>().Object;
var actionDescriptor = new Mock<Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor>().Object;
var modelState = new ModelStateDictionary();
var actionContext = new ActionContext(httpContext, new Mock<RouteData>().Object, actionDescriptor, modelState);
ProductsController controller = new ProductsController(customDbContextMock.Object, loggerMock.Object, envMock.Object);
controller.pageSize = 3;
controller.ControllerContext = new ControllerContext(actionContext);
var result = await controller.List(1, 2);
My controller logic:
public async Task<IActionResult> List(int Id, int page = 1)
{
if (Id == 0)
{
ViewBag.CategoryName = "Wszystkie produkty";
return View(await _context.ProductCategories.Include(c => c.Product).ToListAsync());
}
return View(await _context.ProductCategories
.Include(p => p.Product)
.Where(pt => pt.CategoryId == Id)
.OrderBy(p=>p.ProductId)
.Skip((page-1)*pageSize)
.ToListAsync());
}
我的问题是,我不能通过使用prodCategoryData
和数据列表作为IAsyncEnumerable
来模拟_context.ProductCategories
。
当我将它们转换为IQueryable
时,我得到了这个错误:
Additional information: The source IQueryable doesn't implement IAsyncEnumerable<WineCom.Models.ProductCategory>. Only sources that implement IAsyncEnumerable can be used for Entity Framework .
使用这个答案中的测试类:
如何使用实体框架核心模拟异步存储库
派生出以下泛型扩展方法
public static Mock<DbSet<T>> ToAsyncDbSetMock<T>(this IEnumerable<T> source)
where T : class {
var data = source.AsQueryable();
var mockSet = new Mock<DbSet<T>>();
mockSet.As<IAsyncEnumerable<T>>()
.Setup(m => m.GetEnumerator())
.Returns(new TestAsyncEnumerator<T>(data.GetEnumerator()));
mockSet.As<IQueryable<T>>()
.Setup(m => m.Provider)
.Returns(new TestAsyncQueryProvider<T>(data.Provider));
mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => data.GetEnumerator());
return mockSet;
}
使用上面的扩展方法,测试可以重构为
[Fact]
public async Task Can_Paginate() {
//Arrange
var products = GetFakeProducts().ToAsyncDbSetMock();
var productCategories = GetFakeProductCategories().ToAsyncDbSetMock();
var customDbContextMock = new Mock<ApplicationDbContext>();
customDbContextMock.Setup(x => x.Products).Returns(products.Object);
customDbContextMock.Setup(x => x.ProductCategories).Returns(productCategories.Object);
//...other code removed for brevity
var controller = new ProductsController(customDbContextMock.Object, loggerMock.Object, envMock.Object);
controller.pageSize = 3;
controller.ControllerContext = new ControllerContext(actionContext);
//Act
var result = await controller.List(1, 2);
//Assert
//...other code removed for brevity
}