2017-04-18 109 views
2

我想要編寫單元測試(與NUnit的)服務層,它的用途:的EntityFramework,DbContextScope和精力 - 例外:的DbContext已配置的單元測試裏面

  1. 實體框架作爲數據訪問層
  2. DbContextScope用於管理DbContext生存期

我還使用Effort.EF6在單元測試中模擬DbContext。不幸的是,我找不到一種方法使DbContextScope與Effort兼容,這樣我就可以正確地測試所有的情況。


概述代碼

服務層包括其中做一些業務邏輯類(服務)。每種方法都被視爲完整的交易,以context.SaveChanges()結束。例如:

private IDbContextScopeFactory _dbContextScopeFactory; 

    public DepartmentsService(IDbContextScopeFactory dbContextScopeFactory) 
    { 
     _dbContextScopeFactory = dbContextScopeFactory; 
    } 

    public BusinessModel.Department Insert(BusinessModel.Department department) 
    { 
     using (var dbContextScope = _dbContextScopeFactory.Create()) 
     { 
      // Validation 
      ValidateAndThrowOnFailure(department, new DepartmentAddValidator()); 

      // Operation 
      DBModel.Department newDepartment = Mapper.Map<DBModel.Department>(department); 

      newDepartment.InsertDateUTC = DateTime.UtcNow; 

      dbContextScope.DbContexts.Get<DPSContext>().Departments.Add(newDepartment); 
      dbContextScope.SaveChanges(); 

      return Mapper.Map<BusinessModel.Department>(newDepartment); 
     } 
    } 

單元測試這樣的方法,我做每個測試之前一些準備:

private IDepartmentsService _departmentsService; 
    private IDbContextScopeFactory _dbContextScopeFactory; 
    private IDbContextFactory _dbContextFactory; 
    private DBModel.DPSContext _dbEntities; 

    [SetUp] 
    public void ReInitializeTest() 
    { 
     // Setup DbContext with Effort.EF6 
     string connStr = ConfigurationManager.ConnectionStrings["DPSContext"].ConnectionString; 
     DbConnection connection = EntityConnectionFactory.CreateTransient(connStr); 
     _dbEntities = new DBModel.DPSContext(connection); 

     // Fill DbContext with in-memory data 
     _dbEntities.Departments.AddRange(DataInitializer.GetDepartments()); 
     _dbEntities.SaveChanges(); 

     // Mock IDbContextFactory so that it returns in-memory context 
     var contextFactoryMock = new Mock<IDbContextFactory>(); 

     contextFactoryMock 
      .Setup(f => f.CreateDbContext<DBModel.DPSContext>()) 
      .Returns(_dbEntities); 

     _dbContextFactory = contextFactoryMock.Object; 

     // Setup DbContextScopeFactory to use mocked context 
     _dbContextScopeFactory = new DbContextScopeFactory(_dbContextFactory); 
     _departmentsService = new DepartmentsService(_dbContextScopeFactory); 
    } 

的測試和問題

下面是一個簡單的單元測試:

[Test] 
    public void Insert_WhenValidModelPassed_ShouldInsertNewRecord() 
    { 
     // Given 
     BusinessModel.Department newDepartment = DataInitializer.GetExampleOfNewDepartment(); 

     // When 
     _departmentsService.Insert(newDepartment); 

     // Then 
     Assert.AreEqual(3, _dbEntities.Departments.Count()); 
    } 

的問題是,測試失敗,異常:

System.InvalidOperationException : The operation cannot be completed because the DbContext has been disposed. 

似乎DbContextScope使用內部Insert方法在內部部署分配上下文在using塊的末端,從而當被調用時的Assert拋出異常。有沒有人遇到過類似的問題,或者只是知道我應該怎麼做才能成功地測試這種類似的情況?

回答

3

對於任何遇到類似問題的人,我已經創建了一個有點骯髒但工作的解決方案(至少我希望如此)。除了我在這個問題中寫的內容,我創建了一個從真實環境派生出來的類,並且使得方法沒有...什麼都不做。我還添加了每個測試結束時調用的方法RealDispose

public class TestableDPSContext : DBModel.DPSContext 
    { 
     public TestableDPSContext(DbConnection connection) 
      : base(connection) 
     { 

     } 

     protected override void Dispose(bool disposing) 
     { 
      // Do nothing 
     } 

     public void RealDispose(bool disposing) 
     { 
      // Invoke real dispose 
      base.Dispose(disposing); 
     } 
    } 

    [TearDown] 
    public void FinishTest() 
    { 
     _dbEntities.RealDispose(false); 
    } 

也許有更好的解決方案,但現在看來,以解決DbContext問題正在測試太早配置。