2013-02-21 17 views
16

Ich habe versucht, nach einem effektiven Weg in Unit-Tests meiner Datenzugriffsschicht in C# zu suchen. Ich bin hauptsächlich ein Java-Entwickler und habe C# nur für ungefähr 6 Monate verwendet, in der Vergangenheit habe ich eine Bibliothek namens DBUnit verwendet, um gegen eine bekannte Zustandsdatenbank zu testen. Ich konnte eine ähnliche aktive Bibliothek nicht finden, die verwendet werden kann, am nächsten zu sein scheint nDBUnit, aber es war jetzt für eine Weile nicht aktiv.Ways of Unit Testing Datenzugriffsschicht

Es scheint eine Menge widersprüchlicher Methoden zu geben, wie und warum in C#. Idealerweise möchte ich die Datenzugriffsebene mithilfe von Mocking testen, ohne eine Verbindung zu einer Datenbank herstellen zu müssen, und anschließend die Speicherprozedur in einer separaten Testgruppe testen.

In dem System, an dem ich gerade arbeite, verwendet die Datenzugriffsebene ADO.net (ohne die Verwendung des Entity Framework), um Speicherprozeduren auf einem SQL Server aufzurufen.

Unten ist ein Beispielcode, mit dem ich arbeiten muss; Um den spöttischen Pfad runter zu gehen, müsste ich in der Lage sein, den SqlCommand zu verspotten (mit IDbCommand) und/oder die SqlConnection zu verspotten.

Also meine Frage ist, was scheint der beste Weg zu sein (wenn es so etwas gibt), dies zu tun? Bisher besteht die einzige Möglichkeit darin, ein Proxy-Objekt zu erstellen, das an den Konstruktor übergeben wird, damit es die verspotteten Sql * -Objekte zum Testen zurückgeben kann.

Ich hatte noch keine Gelegenheit, alle verfügbaren verfügbaren C# Mock-Bibliotheken zu betrachten.

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); 
    } 

    } 

} 

Antwort

1

ich beginnen, indem sie ein IDbConnection dann bekommen Sie grundsätzlich aus, dass der Rest des Codes tun.

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(); 
    } 
} 

Dann können Sie die Fabrik verspotten, die IDbConnection, die IDBTransaction, und andere ADO-Objekte Sie von ihnen erstellen. Wie für eine Mock-Bibliothek verwende ich Moq.

+2

ich denke, das der falsche Weg ist, und führt zu spröde Tests. Siehe meine Antwort für meine Argumentation und ein Beispiel dafür, warum. –

2

Die Aufgabe dieser Ebene besteht darin, den Code mit der Datenbank zu verbinden. Es muss das Wissen über die Verbindung und die Syntax der Datenbank kapseln. In der Regel wird die Domänensprache der Datenbanksprache zugeordnet. Ich betrachte diesen Teil der Komponententests als Integrationstest und prüfe daher, ob das Datenbankschema der realen oder der Testdatenbank entspricht. Mehr zum Thema here.

+0

Danke, ich denke, ich sah zu munch in das, ich wollte das ursprünglich tun, aber mit dem Mangel an solchen Bibliotheken in C# Ich fing an, nach einer Alternative zu suchen. – wenic

19

Es ist schade, dass Sie kein Tool finden, das Ihre Datenbank in einen bekannten Status versetzt und Sie Ihr CustomerRepository gegen die Datenbank laufen lassen können, um das CustomerRepository zu testen. Die Antwort besteht jedoch nicht darin, Mocks zu verwenden, um alle ADO-Aufrufe nachzuahmen. Auf diese Weise erstellen Sie einen Komponententest, der keine Logik wirklich testet: Sie testet nur, dass der Code so geschrieben ist, wie Sie es für geschrieben halten.

Sagen wir, dass ich am Ende schreibe eine SQL-INSERT als mein Befehl, um den Kunden in der SQL-Datenbank zu erstellen. Nehmen wir nun an, dass wir eine Änderung vornehmen, so dass die Tabelle customer verschiedene Felder hat (die unseren INSERT-Befehl unterbricht) und dass wir jetzt eine gespeicherte Prozedur verwenden sollten, um den Kunden zu erstellen. Der Test mit Mocks würde immer noch bestehen, obwohl die getestete Implementierung jetzt unterbrochen ist. Wenn Sie die Implementierung für die Verwendung gespeicherter Prozeduren korrigiert haben, würden Ihre Komponententests jetzt fehlschlagen. Was ist der Sinn eines Komponententests, wenn er weitergeht, wenn er fehlschlägt, aber dann fehlschlägt, wenn Sie das System reparieren?

Weitere mögliche Alternativen finden Sie unter this question. Es sieht so aus, als ob die markierte Antwort darin besteht, DBUnit in C# mit IKVM zu verwenden.

So könnte es alternative Wege sein erkunden weiterhin, aber spöttisch die ADO ruft ist nur zu spröde Tests gehen zu führen, die nicht wirklich wichtig, etwas testen Sie.

+0

Danke, ich habe das über das Wochenende besser betrachtet, ich stimme zu, dass der Test auf eine echte Datenbank zugreifen soll, mit unseren Java-Projekten hat es genau so funktioniert, zumal Tabellen und Spaltennamen sich mit dem Wachstum der Projekte geändert haben. Ich habe mir die IKM-Methode angeschaut. Ich möchte lieber nichts zu komplexes vorstellen, das die anderen Entwickler verwalten und verstehen könnten. – wenic

+0

Genau deshalb sollten Sie einen Dienst erstellen, der ein Repository/DAL verwendet. Sie verspotten das Repository, injizieren es in den Dienst und testen diesen Dienst. Dann testen Sie die Logik im Dienst isoliert. Wenn das Repository bricht (falsche Daten zurückgibt), schlägt die Logik fehl und der Test schlägt fehl. Es ist immer lustig zu sehen, wie Leute sich über ein Repository lustig machen und dann die Rückgabe von diesem Repository bestätigen. Sie testen damit nichts. – Gaui

0

Data Access-Layer Um zu testen, werden Sie eine komplexere Struktur brauchen.

Data Access-Layer werden Referenzen von Repository-Objekte aufrufen. Repo-Objekt ruft Referenzen von Entity Framework DbSets über das UnitOfWork-Entwurfsmuster auf.

Data Access Layer (TOP)
|
UnitOfWork
|
Repository Pattern Klassen
|
EF Kontext
|
Tatsächliche Datenbank

Nachdem Sie die Struktur festgelegt haben, werden Sie die Repository-Klassen nachahmen. Zum Beispiel werden Elemente in die DB eingefügt und gehen stattdessen zum Mock-Objekt. Später werden Sie gegen Ihr Mock-Objekt behaupten, dass das Objekt eingefügt wird oder nicht.

Bitte nehmen Sie sich einen Blick auf Implementing the Repository and Unit of Work Patterns