EntityFramework, DbContextScope 및 Effort - 예외 : DbContext가 단위 테스트 내부에 배치되었습니다.

c# dbcontext effort entity-framework unit-testing

문제

내가 사용하는 서비스 레이어에 대한 단위 테스트 (NUnit와 함께)를 작성하려고합니다 :

  1. 데이터 액세스 계층으로서의 Entity Framework
  2. DbContext 수명을 관리하기위한 DbContextScope

또한 단위 테스트에서 DbContext 를 조롱하기 위해 Effort.EF6 을 사용합니다. 불행히도 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 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 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 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);
        }
    }

Insert 메서드 내부에서 사용되는 DbContextScope는 내부적으로 using 블록의 끝에 할당 된 컨텍스트를 처리하므로 호출 될 때 Assert 가 예외를 throw합니다. 아무도 비슷한 문제를 겪었거나이 시나리오와 유사한 시나리오를 성공적으로 테스트하기 위해 내가해야 할 일을 알고 있습니까?

수락 된 답변

비슷한 문제를 겪고있는 사람이라면, 나는 조금 더러운 - 근사한 해결책을 만들었다. (적어도 그렇게되기를 바란다.) 나는이 질문에서 쓴 것 이외에도 실제 문맥에서 파생 된 클래스를 생성하고 Dispose 메서드로 아무것도 만들지 않았다. 또한 각 테스트가 끝날 때 호출되는 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 가 테스트 초기에 너무 빨리 처리되는 문제를 해결하는 것으로 보입니다.




아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.
아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.