2010-07-28 5 views
7

프로젝트의 데이터 액세스 레이어를 테스트하는 유닛을 읽었습니다.유닛 데이터 액세스 레이어 테스트

  • 사용하는 모든 단위 테스트의 마무리 단계에서 전용 테스트 데이터베이스 만 정리 (또는 수동으로 할)
  • 사용에게 데이터베이스를하지만, 커밋하지 않거나 단순히 다시
  • 롤 : 대부분의 옵션은 졸이다
  • 데이터베이스 모의

이전 프로젝트에서 우리는 롤백 방식을 사용했지만 다른 옵션과 이것이 가장 잘 수행되는 방법에 대해 더 알고 싶습니다. 샘플/기사/비디오 /가 있다면 ... 공유하십시오.

답변

3

프로젝트에는 2 가지 유형의 테스트가 있어야합니다. 유닛 테스트 및 통합 테스트

유닛 테스트는 데이터 액세스 및 프리젠 테이션에 대한 의존성없이 프로젝트의 한 측면을 테스트합니다. 단위 테스트의 경우 데이터베이스와 사용자 종속 관계 삽입을 조롱하여 데이터 공급자로부터 코드를 분리합니다. 이렇게하면 더 나은 아키텍처가 생기고 원하는 경우 다른 데이터 공급자를 연결할 수 있습니다. 예 : ADO.net에서 nHibernate로 이동.

통합 테스트는 전체 시스템을 테스트하고 코드가 데이터베이스에서 올바른 데이터를 가져올 수 있는지 확인하는 테스트입니다. 통합 테스트의 경우 각 개발자는 테스트 할 수있는 워크 스테이션에 데이터베이스 사본을 가지고 있어야합니다. 데이터베이스의 생성 및 채우기를 자동화하여 데이터베이스의 훌륭한 복사본으로 빠르고 쉽게 돌아갈 수 있도록해야합니다. nantDBFit 같은 도구는 데이터베이스 작성을 스크립팅하는 데 도움이됩니다.

다른 개발자가 동시에 테스트 할 수있는 상태이므로 중앙 데이터베이스를 사용하지 않아 좋은 상태가 아닐 수 있으며 잘못된 반응을 보일 수 있으며 문제를 디버그하려고 할 수 있습니다. 이는 문제가되지 않습니다.

2

대신 테스트 데이터베이스를 사용하는 것이 좋습니다 ( 아이디어를 저 지르지 마십시오).

My dev 데이터베이스에는 더미 레코드 또는 프로덕션 데이터의 완전하게 샘플링 된 샘플링이 있습니다.

통합 테스트 데이터베이스는 실제 프로덕션 데이터베이스의 복사본입니다 (이 버전은 라이브 변경을 롤백하기 직전에 테스트 한 버전입니다).

0

데이터베이스를 조롱했습니다. 테스트에서 데이터베이스를 다루는 것은 데이터베이스를 생성하고, 스키마를 생성 한 다음 드롭하고, 연결이 끊어지지 않았는지 확인하는 등의 과정을 거쳐야 고통을 받는다.

나를 괴롭히는 또 다른 사실은 코드 논리를 확인하는 것이 코드에서 "너무 멀리"있다는 사실입니다. mockable 클래스 뒤에 SQL 함수 (연결, 명령 등)를 넣고 DAL이 올바른 메서드를 호출하는지 확인합니다. 또한이 방법은 테스트를 훨씬 빠르게 수행합니다.

다음은 몇 가지 빠른 SQL 추상화 클래스 및 예제 사용 + 단위 테스트입니다.

public class SqlConnectionBase : IDisposable { 
    private readonly SqlConnection m_Connection; 

    public SqlConnectionBase(string connString) { 
     m_Connection = new SqlConnection(connString); 
    } 

    public virtual SqlConnection Object { get { return m_Connection; } } 

    public virtual void Open() { 
     m_Connection.Open(); 
    } 
    public virtual void Close() { 
     m_Connection.Close(); 
    } 

    #region IDisposable Members 

    public virtual void Dispose() { 
     m_Connection.Dispose(); 
    } 

    #endregion 
} 

public class SqlCommandBase : IDisposable{ 
    private readonly SqlCommand m_Command; 

    public SqlCommandBase() { 
     m_Command = new SqlCommand(); 
    } 
    public SqlCommandBase(string cmdText, SqlConnectionBase connection) { 
     m_Command = new SqlCommand(cmdText, connection.Object); 
    } 
    public SqlCommandBase(SqlConnectionBase connection) { 
     m_Command = new SqlCommand(); 
     m_Command.Connection = connection.Object; 
    } 

    public virtual int ExecuteNonQuery() { return m_Command.ExecuteNonQuery(); } 
    public virtual string CommandText { get { return m_Command.CommandText; } set { m_Command.CommandText = value; } } 

    public virtual void AddParameter(SqlParameter sqlParameter) { 
     m_Command.Parameters.Add(sqlParameter); 
    } 

    #region IDisposable Members 

    virtual public void Dispose() { 
     m_Command.Dispose(); 
    } 

    #endregion 
} 
public class SqlFactory { 
    public virtual SqlCommandBase CreateCommand(string query, SqlConnectionBase conn) { 
     return new SqlCommandBase(query, conn); 
    } 
    public virtual SqlCommandBase CreateCommand(SqlConnectionBase conn) { 
     return new SqlCommandBase(conn); 
    } 

    public virtual SqlConnectionBase CreateConnection(string connString) { 
     return new SqlConnectionBase(connString); 
    } 

} 

public class DBUser { 
    public DBUser(SqlFactory factory) { 
    m_factory = factory; //dependency constructor, will be used during unit testing 
    } 

    public DBUser() { 
    m_factory = new SqlFactory(); //used during normal execution 
    } 

    public void DoSomething() { 
    var conn = m_factory.CreateConnection("server=servername,database=..."); 
    var cmd = m_factory.CreateCommand(conn); 
    cmd.CommandText = "Select * from users"; 
    cmd.ExecuteNonQuery(); 
    } 

    [TestMethod] 
    public void DoSomethingTest() { 
    var factoryMock = new Mock<SqlFactory>(); 
    var cmdMock = new Mock<CommandBase>(); 
    factoryMock.Setup(f=>f.CreateConnection(It.IsAny<string>())).Returns(cmdMock.Object); 
    DBUser dbUser = new DBUser(factoryMock.Object); 
    dbUser.DoSomething(); 

    //Verify that DoSomething is called. 
    cmdMock.Verify(c=>c.DoSomething()); 
    } 
} 
+0

테스트가 훨씬 빠르게 실행됩니다. 상상할 수 있습니다. 설명 한 내용의 샘플 코드가 있습니까? – XIII

+0

나는 조각이 있는데, [System.IO.Abstractions] (http://systemioabstractions.codeplex.com/)와 비슷한'System.Sql.Abstractions' 라이브러리 작성을 진지하게 고려하고 있습니다. –

1

DAL의 주된 책임은 데이터베이스에서 데이터를 유지/인출하여 테스트중인 시스템이 DAL + 데이터베이스입니다. 데이터베이스 모의를 사용하여 DAL에 테스트를 작성하지 않아도됩니다 - 실제로 누가 특정 엔터티를 가져 오기 위해 어떤 SQL 쿼리를 실행했는지 신경 쓰는 사람이 있습니까?올바른 엔티티가 선택되었고 모든 속성이 올바르게 맵핑되었는지를 검증하는 것이 필요합니다.

이렇게하려면 보통 데이터베이스를 정리하고 테스트 데이터로 데이터베이스를 채우고 DAL 메서드로 데이터베이스를 가져옵니다. 이 테스트의

 [SetUp] 
    public void SetUp() 
    { 
     Database.Clear(); 
     manager = new ServiceManager(); 
    } 

    [TearDown] 
    public void TearDown() 
    { 
     manager.Dispose(); 
    } 

    [Test] 
    public void InsertAndLoadOrderContract() 
    { 
     MinOrderModel model = new OrderBuilder().InsertMinimalOrder(manager); 

     Contract contract = TestObjectGenerator.GenerateEntity<Contract>(); 
     manager.Contract.InsertOrderContract(model.Order.OrderCompositeId, contract); 

     Contract selectedContract = manager.Contract.SelectById(contract.ContractId); 

     AssertContract(selectedContract, contract); 
    } 

    private static void AssertContract(IContract actual, IContract expected) 
    { 
     Assert.That(actual.AgencyCodeOther, Is.EqualTo(expected.AgencyCodeOther)); 
     Assert.That(actual.AgencyFK, Is.EqualTo(expected.AgencyFK)); 
     Assert.That(actual.ContractId, Is.EqualTo(expected.ContractId)); 
     Assert.That(actual.Ident, Is.EqualTo(expected.Ident)); 
    } 

일부 부품은 더 편리 것들에 교체 할 수 있습니다 :

  1. 데이터베이스하지만 롤 을 청소하지 마십시오 데이터베이스를 을 채우기 위해 DbUnit을 사용할 수있다 "TearDown"의 백 트랜잭션 메서드
  2. 더 편리한 데이터베이스 엔진 (예 : SQLLite)을 사용하십시오.
관련 문제