2017-04-18 49 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问题正在测试太早配置。