2016-09-14 77 views
6

我第一次嘗試.NET核心,並看到如何在單元測試中使用Moq。開箱即用,在創建控制器,其中ApplicationDbContext是參數的構造函數是這樣的:我如何在.NET核心Moq ApplicationDbContext

public class MoviesController : Controller 
{ 
    private readonly ApplicationDbContext _context; 

    public MoviesController(ApplicationDbContext context) 
    { 
     _context = context;  
    } 

這裏的單元測試,我開始測試控制器時:

[TestClass] 
public class MvcMoviesControllerTests 
{ 
    [TestMethod] 
    public async Task MoviesControllerIndex() 
    { 
     var mockContext = new Mock<ApplicationDbContext>();    
     var controller = new MoviesController(mockContext.Object); 

     // Act 
     var result = await controller.Index(); 

     // Assert 
     Assert.IsInstanceOfType(result, typeof(ViewResult)); 
    } 

但隨後我意識到ApplicationDbContext是一個具體的類,它沒有無參數的構造函數,所以測試不起作用。它給了我錯誤:無法找到無參數的構造函數。

也許這可能是一個更多針對Moq的問題,而不是它與.NET Core相關,但我也是Moq的新手,所以我不知道如何繼續。下面是當我創建項目ApplicationDbContext代碼是如何產生的:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser> 
{ 
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) 
     : base(options) 
    { 
    } 

    protected override void OnModelCreating(ModelBuilder builder) 
    { 
     base.OnModelCreating(builder); 
     // Customize the ASP.NET Identity model and override the defaults if needed. 
     // For example, you can rename the ASP.NET Identity table names and more. 
     // Add your customizations after calling base.OnModelCreating(builder); 
    } 

    public DbSet<Movie> Movie { get; set; } 
} 

需要什麼改變讓我的單元測試會成功嗎?

UPDATE:

我從https://msdn.microsoft.com/en-us/magazine/mt703433.aspx發現,您可以配置EF核心使用內存數據庫進行單元測試。所以我改變我的單元測試看起來像這樣:

[TestMethod] 
    public async Task MoviesControllerIndex() 
    {   
     var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>(); 
     optionsBuilder.UseInMemoryDatabase(); 
     var _dbContext = new ApplicationDbContext(optionsBuilder.Options); 

     var controller = new MoviesController(_dbContext); 

     // Act 
     var result = await controller.Index(); 

     // Assert 
     Assert.IsInstanceOfType(result, typeof(ViewResult)); 
    } 

這個測試現在成功了。但是,這是做這件事的正確方法嗎?顯然,我完全消除了用Moq嘲笑ApplicationDbContext!還是有另一種解決方案來解決這個問題使用Moq。

+0

即使使用內存中的DbContext,您也基本上正在進行集成測試,這仍然是必需的。然而,從設計的角度來看,你應該讓你的課程取決於抽象而不是結核。創建一個接口,公開您需要的功能並讓您的具體上下文繼承於此。 – Nkosi

+1

我同意@Nkosi這是現在有效的集成測試。當ApplicationDbContext實現'IdentityDbContext '時,你可能可以將接口注入到你的控制器中,然後模擬它嗎? – Corporalis

回答

4

您不應該嘗試直接模擬數據庫上下文。而是實施Repository Pattern並改爲替代模型庫。

這裏有一個great guide關於如何正確實現模式。

4

嘲笑DbContext不起作用,因爲有太多的提供者需要使它工作。更簡單的解決方案是使用Microsoft爲此確實目的而實施的InMemory解決方案。請不要創建僅用於測試的回購(它仍不會測試EF代碼)。

下面是如何與InMemory數據庫在.NET核心

https://docs.efproject.net/en/latest/miscellaneous/testing.html

1

如果有人有興趣,我@Corporalis的理念的一部分,並實現了一個接口暴露ApplicationDbContext測試鏈路測試項目。

public interface IApplicationDbContext 
{ 
    DbSet<Movie> Movies { get; set; } 

    int SaveChanges(); 
} 

而且在ApplicationDbContext

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IApplicationDbContext 
{ 
    public virtual DbSet<Movie> Movies { get; set; } 

    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) 
     : base(options) 
    { 
    } 

    protected override void OnModelCreating(ModelBuilder builder) 
    { 
     base.OnModelCreating(builder); 
    } 
} 

鴕鳥政策forguet註冊在啓動時的界面。CS,ConfigureServices

services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>()); 

所以,現在在控制器或服務,你擁有的每使用ApplicationDbContext方法,這樣做是爲了在接口的引用,而不是

public class filterMovies 
{ 
    private readonly IApplicationDbContext _context 

    public filterMovies(IApplicationDbContext context) 
    { 
    _context = context; 
    } 
} 

現在我們隨意嘲笑使用接口,而不是實現它的自我

var mockContext = new Mock<IApplicationDbContext>(); 
mockContext.Setup(mc => mc.Movies).Returns(mockDbSet.Object); 

的ApplicationDbContext我希望它能幫助;)