2016-11-09 64 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()); 
} 

我的問題是,我無法通過使用prodCategoryData和數據列表作爲IAsyncEnumerable嘲笑_context.ProductCategories

當我將它們轉換成IQueryable我得到這個錯誤,而不是:

附加信息:源的IQueryable沒有實現IAsyncEnumerable。只有實現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

如果您不想編寫大量基礎架構代碼,則可以使用第三方斷言庫,如https://MyTestedASP.NET的MyTested.AspNetCore.Mvc。它會自動嘲笑你的數據庫對象。這裏有一個例子:

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>(); 
相關問題