Asp.Net Core unit testing async controller

匿名 (未验证) 提交于 2019-12-03 01:12:01

问题:

I have this test:

[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()); } 

My problem is that I cannot mock _context.ProductCategories by using prodCategoryData and data lists as IAsyncEnumerable.

When I cast them to IQueryable I get this error instead:

Additional information: The source IQueryable doesn't implement IAsyncEnumerable. Only sources that implement IAsyncEnumerable can be used for Entity Framework .

回答1:

Using the test classes from this answer :

How to mock an async repository with Entity Framework Core

The following generic extension method was derived

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; } 

With the above extension method the test can be refactored to

[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 } 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!