11

Linq-Entities (EF4)와 Linq-to-Objects 간의 잠재적 인 차이로 인해 실제 쿼리를 사용하여 쿼리 클래스가 데이터를 검색하는지 확인해야합니다 올바르게 EF에서. SQL CE 4는 이것에 대한 완벽한 도구 인 것처럼 보이지만 몇 가지 딸꾹질이 있습니다. 이 테스트는 MsTest를 사용합니다.기능 테스트를 위해 SQL CE 4 데이터베이스를 사용하는 방법

데이터베이스가 모델 변경으로 인해 다시 생성되지 않으면 각 테스트 후에 데이터가 제거되지 않고 데이터가 데이터베이스에 계속 추가됩니다. 이로 인해 테스트에서 충돌이 발생할 수 있으며 의도 한 것보다 많은 데이터가 쿼리에서 반환됩니다.

첫 번째 아이디어는 TestInitialize 메서드에서 TransactionScope을 초기화하고 트랜잭션을 TestCleanup에 처리하는 것이 었습니다. 아쉽게도 SQL CE4는 트랜잭션을 지원하지 않습니다.

내 생각에 TestCleanup의 데이터베이스를 File.Delete() 호출을 통해 삭제하는 것이 었습니다. 첫 번째 테스트의 TestCleanup이 데이터베이스를 삭제하는 것으로 보이지만 첫 번째 테스트 이후의 모든 테스트는 데이터베이스를 다시 만들지 않는 것으로 보이므로 불행히도 이것은 첫 번째 테스트가 실행 된 후에 작동하지 않는 것으로 보입니다. 따라서 데이터베이스 파일을 찾을 수 없습니다.

나는 내 테스트 클래스 ClassInitializeClassCleanupTestInitializeTestCleanup 태그를 변경하려고 시도했지만 그 때문에 이전에 ClassInitialize이 (정도가 나타납니다 실행 테스트에 NullReferenceException에 오류가 발생한. ClassInitialize은 어쩌면 그건 기본 클래스에 그것을 일으키는 것).

SQL CE를 효과적으로 테스트하는 방법이 부족합니다. 누구든지 더 좋은 아이디어가 있습니까?


편집 : 해결책을 찾아 냈습니다. 내 EF 유닛 테스트 기본 클래스에서 데이터 컨텍스트의 새 인스턴스를 시작한 다음 context.Database.Delete()context.Database.Create()을 호출합니다. 단위 테스트는 조금 느려지지만, 지금은 단위 테스트를 효율적으로 실제 데이터베이스


최종 편집 사용 할 수 있습니다 앞뒤로 마이크로 소프트와 일부 이메일 후를, 그것은 TransactionScope의 지금 SQLCE에서 허용되는 것으로 나타났다 SqlCE의 최신 릴리스와 함께. 그러나 EF4를 사용하는 경우 트랜잭션을 시작하기 전에 명시 적으로 데이터베이스 연결을 열어야한다는 점에서 몇 가지 제한 사항이 있습니다.

System.Data.Entity.Database.DbDatabase.SetInitializer<YourEntityFrameworkClass>(
    new System.Data.Entity.Database.DropCreateDatabaseAlways<YourEntityFrameworkClass>()); 

이 엔티티 프레임 워크는 항상 다시하게됩니다 : 당신의 TestInitialize 다음을 수행해야합니다에서

[TestMethod] 
    public void My_SqlCeScenario() 
    { 
     using (var context = new MySQLCeModelContext()) //ß derived from DbContext 
     { 
      ObjectContext objctx = ((IObjectContextAdapter)context).ObjectContext; 
      objctx.Connection.Open(); //ß Open your connection explicitly 
      using (TransactionScope tx = new TransactionScope()) 
      { 

       var product = new Product() { Name = "Vegemite" }; 
       context.Products.Add(product); 
       context.SaveChanges(); 
      } 
      objctx.Connection.Close(); //ß close it when done! 
     } 
    } 
+0

물론 SQL CE는 트랜잭션을 지원하지만 TransactionScope를 사용하는 것은 매우 잘못된 방법입니다. 그냥 Connection 객체를 통해 정상적으로 처리하면됩니다. – leppie

+0

'SaveChanges()'를 호출하지 않는 한,'TransactionScope'가없는 EF4 엔티티의 경우 확실하지 않습니다. 이는 테스트가 유효한 테스트가 아님을 의미합니다. – KallDrexx

+0

Sql CE로 데이터를 시드하는 방법에 대한 예제를 제공 할 수 있습니까? 나는 EF6을 사용하여 SQL ce –

답변

4

을 : 다음 코드는 성공적으로 장치/기능 시험은 SQL CE를 사용하는 방법에 대한 예를 보여줍니다 테스트가 실행될 때마다 데이터베이스.

덧붙여 DropCreateDatabaseAlways을 상속하는 대체 클래스를 만들 수 있습니다. 이렇게하면 매번 설정된 데이터로 데이터베이스를 시드 할 수 있습니다.

public class DataContextInitializer : DropCreateDatabaseAlways<YourEntityFrameworkClass> { 
    protected override void Seed(DataContext context) { 
     context.Users.Add(new User() { Name = "Test User 1", Email = "[email protected]" }); 
     context.SaveChanges(); 
    } 
} 

는 그런 다음 초기화 당신이 호출 변경됩니다 :

System.Data.Entity.Database.DbDatabase.SetInitializer<YourEntityFrameworkClass>(
    new DataContextInitializer()); 
+0

을 사용하여 테스트하고 싶다. 마침내 이것을 체크 할 기회를 얻은 후에, 이것이 효과가없는 것 같다. 어떤 테스트를 실행하기 전에 데이터베이스를 삭제하는 것으로 보이지만 각 테스트를 개별적으로 실행하기 전에 데이터베이스를 삭제하지는 않습니다. 이로 인해 단위 테스트에서 데이터가 지속되어 일부 다른 테스트가 실패하게됩니다. 나는 지금 당장이 문제를 해결할 수있다. – KallDrexx

+0

Nevermind - 너무 복잡하고 단위 테스트에 잘 빌려주지 않습니다 ... 기본적으로 여러 테이블을 단일 엔티티로 매핑하는 데 사용합니다. 특정 테이블에 대해 호출 할 때마다 새 어셈블리에서 처리됩니다 ... 정말 설명하기가 어렵습니다 :) – Buildstarted

+0

각 테스트 후 테이블을 지우고 seed 메소드로 다시 채울 수는 없습니까? 또는 각각의 테스트가 다른 테이블 세트와 무엇이 아닌지? – Buildstarted

3

내가 "최종 편집"의 방법을 발견을뿐만 아니라 나를 위해 작동합니다. 그러나, 그것은 정말로 짜증나게합니다. 테스트를위한 것이 아니라 Entity Framework 및 SQL CE와 함께 TransactionScope를 사용하고자 할 때 유용합니다. 한 번 코딩하고 내 응용 프로그램에서 SQL Server와 SQL CE를 모두 지원하도록하고 싶지만 어디에서나이 작업을 수행해야합니다. 확실히 Entity Framework 팀이이를 처리해야합니다!

그동안 코드에서 코드를 조금 더 깨끗하게 만들기 위해 한 걸음 더 다가갔습니다.

public MyDataContext() 
{ 
    this.Connection.Open(); 
} 

protected override void Dispose(bool disposing) 
{ 
    if (this.Connection.State == ConnectionState.Open) 
     this.Connection.Close(); 

    base.Dispose(disposing); 
} 

private DbConnection Connection 
{ 
    get 
    { 
     var objectContextAdapter = (IObjectContextAdapter) this; 
     return objectContextAdapter.ObjectContext.Connection; 
    } 
} 

당신이 실제로 사용할 때이 많이 청소기를 만드는 : (당신이 DbContext에서 파생 된 어떤 클래스) 데이터 컨텍스트에이 블록을 추가

using (var db = new MyDataContext()) 
{ 
    using (var ts = new TransactionScope()) 
    { 
     // whatever you need to do 

     db.SaveChanges(); 
     ts.Complete(); 
    } 
} 

내 생각하지만 당신이 디자인하는 경우 귀하 모든 변경 사항이 SaveChanges()에 대한 단일 호출에서 커밋되도록하려면 암시 ​​적 트랜잭션으로 충분할 것입니다. 테스팅 시나리오에서는 ts.Complete()를 호출하는 대신 모든 것을 롤백하기를 원하므로 확실히 필요하다. 우리가 사용할 수있는 트랜잭션 범위가 필요한 다른 시나리오가 있다고 확신합니다. EF/SQLCE에서 직접 지원하지 않는 것은 부끄러운 일입니다.

+0

흥미로운 솔루션. 방금'IUnitOfWork' 클래스가'BeginTransaction()'과'EndTransaction (bool commit)'메소드를 가지고있는 문제를 해결했습니다. 'BeginTransaction'은 아직 존재하지 않으면 연결을 열고,'EndTransaction'은 트랜잭션을 쉽게 완료하거나 롤백 할 수있게합니다. 지금까지 이것은 잘 작동하는 것 같습니다. 다행히도 앞으로는 트랜잭션 스코프를 단독으로 사용할 수도 있습니다. – KallDrexx

관련 문제