2014-10-10 52 views
3

我是新來模擬和使用Moq。這是我第一次嘗試模擬添加功能。我有模擬設置,它適用於讀取功能,但是當我嘗試單元測試一個添加方法時,它將更改保存到數據庫。我的Mocked DbContext使用Moq在單元測試中寫入數據庫

如何模擬添加方法?

我想我有嘲笑我UnitOfWork類有SaveChanges()方法。我想知道如何設置模擬攔截SaveChanges()的呼叫,而不是保存到分貝。

這裏是嘲笑我到目前爲止

[SetUp] 
    public void SetUp() 
    { 
     addCount = 0; 

     IEnumerable<Platform> platformList = new List<Platform>(){ 
      new Platform() { Id = 1, Name = "Unknown"}, 
      new Platform() { Id =2, Name = "Amazon"}, 
      new Platform() { Id = 3, Name = "Prime Pantry"} 
     }; 
     var platformData = platformList.AsQueryable(); 

     var mockPlatformSet = new Mock<DbSet<Platform>>(); 
     mockPlatformSet.As<IQueryable<Platform>>().Setup(m => m.Provider).Returns(platformData.Provider); 
     mockPlatformSet.As<IQueryable<Platform>>().Setup(m => m.Expression).Returns(platformData.Expression); 
     mockPlatformSet.As<IQueryable<Platform>>().Setup(m => m.ElementType).Returns(platformData.ElementType); 
     mockPlatformSet.As<IQueryable<Platform>>().Setup(m => m.GetEnumerator()).Returns(platformData.GetEnumerator()); 
     mockPlatformSet.Setup(m => m.Add(It.IsAny<Platform>())).Callback(() => addCount++); 

     var mockContext = new Mock<ApplicationDbContext>(){ CallBase = true }; 
     mockContext.Setup(m => m.Platforms).Returns(mockPlatformSet.Object); 
     mockContext.Setup(m => m.Platforms.Add(It.IsAny<Platform>())); 
     mockContext.Setup(m => m.Platforms.Add(It.IsAny<Platform>())).Callback(() => addCount++); 


     unitOfWork = new UnitOfWork(mockContext.Object); 
     platformRepo = new PlatformRepository(mockContext.Object); 

     controller = new PlatformController(platformRepo, unitOfWork); 
    } 

添加的UnitOfWork代碼

public class UnitOfWork : IUnitOfWork 
{ 
    private readonly DbContext _context; 
    private bool _isDisposed = false; 

    public UnitOfWork(DbContext context) 
    { 
     _context = context; 
    } 

    public void SaveChanges() 
    { 
     _context.SaveChanges(); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     if (!_isDisposed) 
     { 
      if (disposing) 
      { 
       _context.Dispose(); 
      } 
     } 
     _isDisposed = true; 
    } 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

} 

更新問題

我試圖單元測試我的Create方法我PlatformController。在這種方法中,我調用Add函數,然後SaveChanges函數UnitOfWork。我想驗證我的Platform對象被添加到DbSet中,但是「截取」SaveChanges()的調用,以致它不寫入數據庫。

我該怎麼做?

+0

當我試圖做嘲諷的代碼,我嘲笑上下文集合來代替。 – Ian 2014-10-10 15:36:59

+0

您可以添加上下文被注入的'UnitOfWork'構造函數的代碼嗎? – elolos 2014-10-10 15:47:26

+0

@elolos我添加了UnitOfWork代碼 – DFord 2014-10-10 15:50:04

回答

5

當我CallBase值的我mockContext作爲IUnitOfWork設置爲false,這似乎已經解決了我的單元測試寫入到數據庫的問題。

這行代碼: mockContext.As<IUnitOfWork>().CallBase = false;

這裏是我的Setup功能

[SetUp] 
    public void SetUp() 
    { 
     addCount = 0; 

     IEnumerable<Platform> platformList = new List<Platform>(){ 
      new Platform() { Id = 1, Name = "Unknown"}, 
      new Platform() { Id =2, Name = "Amazon"}, 
      new Platform() { Id = 3, Name = "Prime Pantry"} 
     }; 
     var platformData = platformList.AsQueryable(); 

     var mockPlatformSet = new Mock<DbSet<Platform>>(); 
     mockPlatformSet.As<IQueryable<Platform>>().Setup(m => m.Provider).Returns(platformData.Provider); 
     mockPlatformSet.As<IQueryable<Platform>>().Setup(m => m.Expression).Returns(platformData.Expression); 
     mockPlatformSet.As<IQueryable<Platform>>().Setup(m => m.ElementType).Returns(platformData.ElementType); 
     mockPlatformSet.As<IQueryable<Platform>>().Setup(m => m.GetEnumerator()).Returns(platformData.GetEnumerator()); 
     mockPlatformSet.Setup(m => m.Add(It.IsAny<Platform>())).Callback(() => addCount++); 

     var mockContext = new Mock<ApplicationDbContext>(){ CallBase = true }; 
     mockContext.Setup(m => m.Platforms).Returns(mockPlatformSet.Object); 
     mockContext.Setup(m => m.Platforms.Add(It.IsAny<Platform>())); 
     mockContext.Setup(m => m.Platforms.Add(It.IsAny<Platform>())).Callback(() => addCount++); 
     mockContext.Setup(m => m.Set<Platform>()).Returns(mockPlatformSet.Object); 
     mockContext.As<IUnitOfWork>().CallBase = false; 

     unitOfWork = new UnitOfWork(mockContext.Object); 
     platformRepo = new PlatformRepository(mockContext.Object); 

     controller = new PlatformController(platformRepo, unitOfWork); 
    } 
3

我不知道這是你要找的答案,但有工作單位的抽象EF DbContext是一個可怕的想法。原因是已經在的背景下是工作單元實施。根據班級的msdn說明:

表示單位作業內容和信息庫模式 的組合,使您能夠查詢數據庫和組一起改變了 將被寫回作爲一個單位存儲。

一旦你刪除了不必要的抽象,嘲笑上下文應該相當容易,尤其是如果你使用最新版本的實體框架。

+0

謝謝你的建議,但對於這個不會發生的項目。這個抽象過於集成,現在要用時間線去除。 – DFord 2014-10-10 15:48:12

0

你想在這裏做功能測試,所以這將是明智的,有功能的數據庫。

EF可以重新和你的安裝和拆卸方法與測試連接字符串摧毀你的數據庫。這將爲您的測試提供一個真實的功能測試環境,以模擬真實的環境。

例:

[TestFixtureSetUp] 
    public static void SetupFixture() //create database 
    { 
     using (var context = new XEntities()) 
     { 
      context.Setup(); 
     } 
    } 

    [TestFixtureTearDown] 
    public void TearDown() //drop database 
    { 
     using (var context = new XEntities()) 
     { 
      context.Database.Delete(); 
     } 
    } 

    [SetUp] 
    public void Setup() //Clear entities before each test so they are independent 
    { 
     using (var context = new XEntities()) 
     { 
      foreach (var tableRow in context.Table) 
      { 
       context.Table.Remove(tableRow); 
      } 
      context.SaveChanges(); 
     } 
    } 

更改連接字符串中您的測試項目,指向「DbNameTest」,你是金使用您的XEntities類在測試中,這將是明確的建立和加入測試數據與之交互。

+0

我認爲他想寫單元測試,而不是集成/功能測試。 – elolos 2014-10-10 15:36:02

+0

是的,但是當你測試數據庫交互的功能時,你正在做一個功能測試。他可能認爲他想要單元測試,但他實際上在調用add()時測試功能。通過這種方式,他可以不用擊中他的prod DB,而是通過EF爲這個功能測試創建一個測試數據庫。 – 2014-10-10 15:42:50

+1

我認爲大多數項目都需要。沒有'Assert'語句,但是如果他想單獨測試控制器邏輯,那麼這是一個單元測試。 – elolos 2014-10-10 15:45:13