2010-07-28 17 views
7

Czytałem o testowaniu jednostki warstwy dostępu do danych projektu. Większość opcji sprowadzają się do:Jednostka testująca warstwę dostępu do danych

  • użyć dedykowanego testowej bazy danych, ale porządki w fazie finalizacji wszystkich testów jednostkowych (lub zrobić to ręcznie)
  • korzystać z bazy danych, ale nie popełnić albo po prostu wycofać
  • Makiety bazy danych

W poprzednim projekcie używaliśmy metody wycofywania, ale chciałbym dowiedzieć się więcej o innych opcjach i ich najlepszych wynikach. Jeśli masz próbki/artykuły/filmy/... proszę, udostępnij je.

Odpowiedz

3

Musisz mieć 2 rodzaje testów z projektem. testy jednostkowe i testy integracji

Testy jednostkowe testują jeden aspekt projektu bez zależności dostępu do danych i prezentacji. W przypadku testów jednostkowych kpisz z bazy danych i zastrzyku zależności użytkownika, aby odłączyć swój kod od dostawcy danych. To prowadzi do lepszej architektury i umożliwia podłączenie innego dostawcy danych, jeśli sobie tego życzysz. na przykład przejście z ADO.net do nHibernate.

Testy integracji polegają na przetestowaniu całego systemu i upewnieniu się, że kod może uzyskać poprawne dane z bazy danych itp. W przypadku testów integracyjnych każdy programista powinien mieć kopię bazy danych na stacji roboczej, którą może przetestować. Powinieneś spróbować zautomatyzować tworzenie i populację bazy danych, abyś mógł szybko i łatwo wrócić do dobrej kopii bazy danych. Narzędzia takie jak nant i DBFit pomogą Ci w skryptowaniu tworzenia baz danych.

Nie będę używać centralnej bazy danych do testowania, ponieważ inni programiści mogą testować ją w tym samym czasie i może nie być w dobrym stanie i można uzyskać fałszywe alarmy i spędzić wieki próbując usunąć problem co nie stanowi problemu.

2

Wolę używać testowej bazy danych zamiast , nie popełniać pomysłu.

Moja baza danych dev zawiera zapisy manekinowe lub w pełni odkażone próbki danych produkcyjnych.

Moja baza danych testów integracji jest kopią rzeczywistej bazy danych produkcji (ta wersja jest używana do testowania tuż przed wprowadzeniem zmian na żywo).

0

Kpiłabym z bazy danych. Radzenie sobie z bazą danych w teście jest bolesne, ponieważ trzeba utworzyć bazę danych, utworzyć schemat, a następnie go usunąć, upewnić się, że nie ma żadnych połączeń, itp. Jest bolesne.

Inną rzeczą, która sprawia, że ​​czuję się niekomfortowo, jest fakt, że weryfikacja logiki kodu jest "zbyt daleko" od kodu. Poszedłem na drogę umieszczania funkcji Sql (połączeń, komend itd.) Za klasą, którą można uzyskać i sprawdzałem, czy DAL wywołuje odpowiednie metody. Również testy przebiegają o wiele szybciej w ten sposób.

Oto kilka szybkich klas abstrakcji sql i przykład użycia + test jednostki.

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

Testy przebiegają o wiele szybciej w ten sposób: mogę sobie wyobrazić. Czy masz przykładowy kod tego, co opisałeś? – XIII

+0

Mam bity i kawałki, poważnie rozważam napisanie biblioteki 'System.Sql.Abstractions', podobnej do [System.IO.Abstractions] (http://systemioabstractions.codeplex.com/) –

1

Głównym zadaniem DAL jest utrwalanie/pobieranie danych z bazy danych, dlatego testowany system to DAL + Baza danych. Nie ma sensu pisać testów na DAL za pomocą makiety bazy danych - kto naprawdę dba o to, jakie kwerendy sql zostały wykonane, aby pobrać konkretny obiekt?Konieczne jest sprawdzenie, czy wybrano prawidłowy obiekt i czy wszystkie atrybuty zostały poprawnie odwzorowane.

W tym celu zwykle czyściłem bazę danych, uzupełniłem bazę danych danymi testowymi i pobierałem ją metodami 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)); 
    } 

Niektóre części tego testu może być wymieniona na bardziej dogodnych nich:

  1. Jest możliwe użycie DBUnit wypełnić się bazę danych z danymi
  2. Nie oczyścić bazę danych, ale rolki Z powrotem transakcję w "TearDown" metoda
  3. Użyj bardziej wygodnego silnika bazy danych do testów (np. SQLLite)
Powiązane problemy