2013-02-21 10 views
16

Próbuję szukać skutecznego sposobu w testowaniu jednostki mojej warstwy dostępu do danych w języku C#. Jestem głównym programistą Java i używam C# tylko przez około 6 miesięcy, w przeszłości używałem biblioteki o nazwie DBUnit do testowania przeciwko znanej, stanowej bazie danych. Nie udało mi się znaleźć podobnej aktywnej biblioteki, która może być użyta, najbliższa wydaje się być nDBUnit, ale od jakiegoś czasu nie była aktywna.Sposoby testowania dostępu do danych na poziomie warstwy

Wydaje się, że istnieje wiele sprzecznych metod dotyczących sposobu i przyczyny w języku C#. Idealnie chcę przetestować warstwę dostępu do danych za pomocą szyderstwa bez konieczności łączenia się z bazą danych, a następnie przetestować procedurę sklepu w osobnym zestawie testów.

W systemie, nad którym pracuję, warstwa dostępu do danych ma używać ADO.net (bez użycia Entity Framework) do wywoływania procedur składowania na serwerze SQL.

Poniżej znajduje się przykładowy kod, nad którym mam pracować; aby przejść przez kpiącą ścieżkę, musiałbym móc wyśmiać SqlCommand (używając IDbCommand) i/lub pozorować SqlConnection.

Moje pytanie brzmi: co wydaje się najlepszym sposobem (jeśli jest coś takiego) do zrobienia tego? Do tej pory jedynym sposobem byłoby przekazanie obiektu Proxy do konstruktora, aby mógł on zwrócić wyśmiewane obiekty Sql * do testowania.

Nie miałem okazji przyjrzeć się jeszcze dostępnym dostępnym bibliotekom C#.

public class CustomerRepository : ICustomerRepository 
{ 
    private string connectionString; 

    public CustomerRepository (string connectionString) 
    { 
    this.connectionString = connectionString; 
    } 

    public int Create(Customer customer) 
    { 

    SqlParameter paramOutId = new SqlParameter("@out_id", SqlDbType.Int); 
    paramOutId.Direction = ParameterDirection.Output; 
    List<SqlParameter> sqlParams = new List<SqlParameter>() 
    { 
     paramOutId, 
     new SqlParameter("@name", customer.Name) 
    } 

    SqlConnection connection = GetConnection(); 
    try 
    { 
     SqlCommand command = new SqlCommand("store_proc_name", connection); 

     command.CommandType = CommandType.StoredProcedure; 

     command.Parameters.AddRange(sqlParams.ToArray()); 

     int results = command.ExecuteNonQuery(); 

     return (int) paramOutId.Value; 
    } 
    finally 
    { 
     CloseConnection(connection); 
    } 

    } 

} 

Odpowiedz

1

I zacząć od coraz IDbConnection następnie można w zasadzie zrobić resztę swojego kodu z tego.

using(IDbConnection conn = factory.GetConnection(connectionstring)) 
{ 
    conn.Open(); 
    using(IDbTransaction trans = conn.BeginTransaction()) 
    { 
     IDbCommand command = conn.CreateCommand(); 
     IDataParameter param = command.CreateParameter(); 
     // Do something with your command 
     trans.Commit(); 
    } 
} 

Następnie można drwić Fabrycznie IDbConnection Z IDBTransaction oraz wszelkie inne obiekty ADO utworzyć z nich. Jeśli chodzi o bibliotekę próbną, używam Moq.

+2

myślę, że to jest złe podejście i prowadzi do testów kruchych. Zobacz moją odpowiedź na moje rozumowanie i przykład tego, dlaczego. –

2

Ta warstwa ma za zadanie połączyć kod z bazą danych. Musi zawierać wiedzę na temat połączenia db i składni. Zazwyczaj odwzorowuje język domeny na język bazy danych. Patrzę na tę część testów jednostkowych jako test integracji i jako taki testuję, że schemat bazy danych jest równoważny z rzeczywistą lub testową bazą danych. Więcej na ten temat: here.

+0

Dzięki, wydaje mi się, że za bardzo się tym zajmowałem, pierwotnie chciałem to zrobić, ale z powodu braku takich bibliotek w C# zacząłem szukać alternatywy. – wenic

19

To niefortunne, że nie możesz znaleźć narzędzia, które umieszcza twoją bazę danych w znanym stanie i pozwala uruchomić Twój CustomerRepository przeciwko bazie danych, aby przetestować CustomerRepository. Jednak odpowiedź nie polega na tym, aby zacząć używać mocków do wyszydzania wszystkich wywołań ADO. W ten sposób kończy się tworzenie testu jednostkowego, który tak naprawdę nie testuje żadnej logiki: testuje tylko, czy kod jest napisany tak, jak myślisz, że powinien zostać napisany.

Załóżmy, że napisałem SQL INSERT jako moje polecenie utworzenia klienta w bazie danych SQL. Teraz załóżmy, że wprowadzamy zmianę, aby tabela klientów zawierała różne pola (które przerywają nasze polecenie INSERT) i że teraz powinniśmy używać procedury przechowywanej do utworzenia klienta. Test z mockami będzie nadal mijał, mimo że jego implementacja jest teraz zepsuta. Ponadto, jeśli poprawiono implementację, aby używać procedur składowanych, testy jednostek zakończyłyby się niepowodzeniem. Jaki jest sens testu jednostkowego, jeśli nadal przechodzi, kiedy powinien zawieść, ale potem zawiedzie, gdy naprawisz system?

Aby uzyskać kilka możliwych alternatyw, patrz this question. Wygląda na to, że zaznaczona odpowiedź faktycznie kończy się używanie DBUnit w języku C# przy użyciu IKVM.

Tak więc, mogą istnieć alternatywne sposoby dalszego odkrywania, ale kpiny z wywołań ADO doprowadzą do kruchego testu, który tak naprawdę niczego nie przetestuje.

+0

Dzięki temu poprawiłem wygląd w weekend, zgadzam się, że test powinien mieć dostęp do prawdziwej bazy danych, z naszymi projektami Java, które zadziałały właśnie tak, zwłaszcza że nazwy tabel i kolumn zmieniły się wraz z rozwojem projektów. Przyjrzałem się metodzie IKM, wolałbym nie wprowadzać niczego zbyt skomplikowanego, aby inni deweloperzy mogli nim zarządzać i rozumieć. – wenic

+0

Właśnie dlatego należy utworzyć usługę korzystającą z repozytorium/DAL. Szydzisz z repozytorium, wstrzykujesz je do usługi i testujesz tę usługę. Następnie testujesz logikę w usłudze w izolacji. Jeśli repozytorium ulegnie uszkodzeniu (zwróci nieprawidłowe dane), logika zakończy się niepowodzeniem i test zakończy się niepowodzeniem. Zawsze jest zabawnie widzieć ludzi kpiących z repozytorium, a następnie potwierdzających zwroty z tego repozytorium. Nie testujesz niczego z tym. – Gaui

0

Aby przetestować warstwę DataAccess potrzebna jest bardziej złożona struktura.

Warstwa DataAccess wywoła odwołania z obiektów repozytorium. Obiekt repo będzie wywoływał odwołania z Entity Framework DbSets za pośrednictwem wzoru projektu UnitOfWork.

DataAccess Layer (TOP)
|
UnitOfWork
|
Klasy wzorów repozytorium
|
Kontekst EF
|
Faktyczna baza danych

Po ustawieniu struktury, będziesz kpić z klas repozytorium. Na przykład elementy zostaną wstawione do DB zamiast przejść do fałszywego obiektu. Później będziesz występował przeciwko swojemu fałszywemu obiektowi, aby zobaczyć element włożony lub nie.

Proszę spojrzeć na Implementing the Repository and Unit of Work Patterns

Powiązane problemy