【问题标题】:ASP.Net Core unit testing async controller [duplicate]ASP.Net Core 单元测试异步控制器 [重复]
【发布时间】:2017-03-22 18:30:36
【问题描述】:

我有这个测试:

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

我的控制器逻辑:

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 模拟_context.ProductCategories,并将数据列表用作IAsyncEnumerable

当我将它们转换为 IQueryable 时,我得到了这个错误:

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

【问题讨论】:

标签: c# unit-testing asp.net-core-mvc moq xunit


【解决方案1】:

使用此答案中的测试类:

How to mock an async repository with Entity Framework Core

衍生出以下通用扩展方法

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
}

【讨论】:

  • 谢谢你,这帮助很大。尽管我确实想知道如果我做这样的事情只是为了测试我的存储库方法是否真的返回了我需要的数据,我是否正确地编写了我的测试。是否有任何合理的替代方法来模拟 DbSet?
猜你喜欢
  • 2019-03-16
  • 2018-05-01
  • 2014-08-21
  • 1970-01-01
  • 1970-01-01
  • 2016-08-03
  • 2017-12-12
  • 2019-08-08
  • 1970-01-01
相关资源
最近更新 更多