3

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<WineCom.Models.ProductCategory>. Only sources that implement IAsyncEnumerable can be used for Entity Framework .
1

1 Answer 1

14

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
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you, this helped immensely. Although I do wonder if I am writing my tests correctly if I have do things like this just to test whether my repository methods actually return the data I need. Are there any reasonable alternatives to mocking DbSet?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.