2016-11-09 6 views
2

У меня есть этот тест:Asp.Net Основной блок тестирования асинхронной контроллер

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

Моя проблема заключается в том, что я не могу издеваться _context.ProductCategories с помощью prodCategoryData и данных списков, IAsyncEnumerable ,

Когда я бросил их IQueryable я получаю эту ошибку вместо:

Дополнительная информация: Источник IQueryable не реализует IAsyncEnumerable. Для Entity Framework могут использоваться только те источники, которые реализуют IAsyncEnumerable.

+0

Посмотрите здесь http://stackoverflow.com/a/40491640/5233410 – Nkosi

ответ

3

Используя тестовые классы из этого ответа:

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 
} 
+0

Спасибо, это помогло очень. Хотя я действительно задаюсь вопросом, правильно ли я пишу свои тесты, если у меня есть подобные вещи, чтобы проверить, действительно ли мои методы репозитория возвращают нужные мне данные. Есть ли разумные альтернативы издевательскому DbSet? – Robula

0

Если вы не хотите писать много кода инфраструктуры, вы можете использовать стороннюю библиотеку утверждений, такую ​​как MyTested.AspNetCore.Mvc, от https://MyTestedASP.NET. Он автоматически издевается над вашими объектами базы данных. Вот пример:

MyMvc 
.Controller<MvcController>() 
.WithOptions(options => options 
    .For<AppSettings>(settings => settings.Cache = true)) 
.WithSession(session => session 
    .WithEntry("Session", "SessionValue")) 
.WithDbContext(db => db.WithEntities(entities => entities 
    .AddRange(SampleDataProvider.GetModels()))) 
.Calling(c => c.SomeAction()) 
.ShouldHave() 
.MemoryCache(cache => cache 
    .ContainingEntry(entry => entry 
     .WithKey("CacheEntry") 
     .WithSlidingExpiration(TimeSpan.FromMinutes(10)) 
     .WithValueOfType<CachedModel>() 
     .Passing(a => a.Id == 1))) 
.AndAlso() 
.ShouldReturn() 
.View() 
.WithModelOfType<ResponseModel>();