2013-02-21 13 views
16

Sto provando a cercare un modo efficace in unità di test del mio livello di accesso ai dati in C#. Sono uno sviluppatore Java primario e ho usato C# per circa 6 mesi, in passato ho usato una libreria chiamata DBUnit per testare un database di stato conosciuto. Non sono stato in grado di trovare una libreria attiva simile che possa essere utilizzata, la più vicina sembra essere nDBUnit ma non è attiva da un po 'di tempo.Modi di accesso ai dati di test del livello di accesso

Sembra esserci un sacco di metodi contrastanti su come e perché in C#. Idealmente, voglio testare il livello di accesso ai dati utilizzando il mocking senza la necessità di connettersi a un database e quindi l'unità testare la procedura di archiviazione in una serie separata di test.

Nel sistema su cui sto lavorando, il livello di accesso ai dati è quello di utilizzare ADO.net (senza l'utilizzo di Entity Framework) per chiamare le stored procedure su un server SQL.

Di seguito è riportato un codice di esempio su cosa devo lavorare; per andare giù per il percorso di derisione, dovrei essere in grado di simulare SqlCommand (usando IDbCommand) e/o prendere in giro SqlConnection.

Quindi la mia domanda è quello che sembra essere il modo migliore (se c'è una cosa del genere) per fare questo? Finora l'unico modo sarebbe stato rendere l'oggetto Proxy passato al costruttore in modo che potesse restituire gli oggetti Sql * falsificati per il test.

Non ho ancora avuto la possibilità di consultare tutte le librerie di mock C# disponibili.

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

    } 

} 

risposta

1

Comincio da ottenere un IDbConnection allora si può sostanzialmente fare il resto del codice da questo.

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

Poi si può deridere la fabbrica, l'IDbConnection, l'IDBTransaction, e gli oggetti di qualsiasi altro ADO create da loro. Per quanto riguarda una libreria Mock, utilizzo Moq.

+2

Credo che questo sia l'approccio sbagliato, e porta a test fragili. Vedi la mia risposta per il mio ragionamento e un esempio del perché. –

2

Il lavoro di questo livello consiste nel connettere il codice al database. Deve incapsulare la conoscenza della connessione e della sintassi db. Di solito mappa la lingua del dominio nella lingua del database. Considero questa parte dei test unitari come test di integrazione, e come tale collaudo che lo schema del database è equivalente al database reale o di test. Maggiori informazioni sull'argomento here.

+0

Grazie, penso di averlo preso troppo in considerazione, inizialmente volevo farlo ma con la mancanza di tali librerie in C# ho iniziato a cercare un'alternativa. – wenic

19

È un peccato che non sia possibile trovare uno strumento che mette il database in uno stato noto e consente di eseguire il CustomerRepository sul database per testare il CustomerRepository. Tuttavia, la risposta non è quella di iniziare a usare i mock per prendere in giro tutte le chiamate ADO. In questo modo, si finisce per creare un test unitario che non mette alla prova alcuna logica: si tratta solo di testare che il codice sia scritto nel modo in cui si pensa che debba essere scritto.

Diciamo che finisco per scrivere un SQL INSERT come comando per creare il cliente nel database SQL. Ora diciamo che stiamo apportando una modifica in modo che la tabella dei clienti abbia campi diversi (che interrompe il nostro comando INSERT) e che ora dovremmo usare una stored procedure per creare il cliente. Il test con i mock sarebbe ancora passato, anche se l'implementazione che sta testando è ora interrotta. Inoltre, se hai corretto l'implementazione per utilizzare stored procedure, i tuoi test unitari ora fallirebbero. Qual è il punto di un test unitario se continua a passare quando dovrebbe fallire, ma poi fallirebbe quando si riparava il sistema?

Vedere this question per alcune possibili alternative. Sembra che la risposta marcata sia effettivamente finire con l'uso di DBUnit in C# usando IKVM.

Quindi, potrebbero esserci strade alternative per continuare a esplorare, ma prendere in giro le chiamate ADO porterà solo a test fragili che non mettono alla prova nulla di importante.

+0

Grazie, ho dato un'occhiata migliore durante il fine settimana, concordo sul fatto che il test dovrebbe accedere a un vero database, con i nostri progetti Java ha funzionato proprio così, specialmente quando i nomi delle tabelle e delle colonne sono cambiati man mano che i progetti crescevano. Ho dato un'occhiata al metodo IKM, preferirei non introdurre nulla di troppo complesso per gli altri sviluppatori da gestire e capire. – wenic

+0

Esattamente, ecco perché è necessario creare un servizio che utilizza un repository/DAL. Si prende in giro il repository, lo si immette nel servizio e si verifica tale servizio. Quindi stai testando la logica del servizio in isolamento. Se il repository si interrompe (restituisce dati non corretti), la logica fallirà e il test fallirà. È sempre divertente vedere la gente deridere un repository e quindi rivendicare i ritorni da quel repository. Non stai provando nulla con quello. – Gaui

0

Per testare il livello DataAccess è necessaria una struttura più complessa.

Il livello DataAccess chiamerà i riferimenti dagli oggetti del repository. L'oggetto Repo chiamerà i riferimenti da DbSet di Entity Framework tramite il modello di progettazione UnitOfWork.

Livello DataAccess (TOP)
|
UnitOfWork
|
Classi pattern repository
|
Contesto EF
|
Database effettivo

Dopo aver impostato la struttura, verranno simulate le classi di repository. Ad esempio, gli elementi saranno inseriti in DB invece andranno a simulare oggetto. Più avanti farai valere contro il tuo oggetto finto per vedere l'oggetto inserito o meno.

Si prega di dare un'occhiata al Implementing the Repository and Unit of Work Patterns

Problemi correlati