Domanda

Sono quasi all'inizio di un nuovo progetto e (sussulto!) per la prima volta in assoluto sto cercando di includere test unitari in un mio progetto.

Sto riscontrando problemi nell'elaborare autonomamente alcuni dei test unitari. Ho alcuni metodi che sono stati abbastanza facili da testare (passare due valori e verificare l'output previsto). Ho altre parti del codice che stanno facendo cose più complesse come l'esecuzione di query sul database e non sono sicuro di come testarle.

public DataTable ExecuteQuery(SqlConnection ActiveConnection, string Query, SqlParameterCollection Parameters)
{
    DataTable resultSet = new DataTable();
    SqlCommand queryCommand = new SqlCommand();
    try
    {
        queryCommand.Connection = ActiveConnection;
        queryCommand.CommandText = Query;

        if (Parameters != null)
        {
            foreach (SqlParameter param in Parameters)
            {
                 queryCommand.Parameters.Add(param);
            }
        }

        SqlDataAdapter queryDA = new SqlDataAdapter(queryCommand);
        queryDA.Fill(resultSet);
    }
    catch (Exception ex)
    {
        //TODO: Improve error handling
        Console.WriteLine(ex.Message);
    }

    return resultSet;
}

Questo metodo essenzialmente utilizza tutti i bit necessari per estrarre alcuni dati dal database e restituisce i dati in un oggetto DataTable.

La prima domanda è probabilmente la più complessa: cosa dovrei anche testare in una situazione come questa?

Una volta risolto, sorge la questione se deridere o meno i componenti del database o provare a testare il DB effettivo.

È stato utile?

Soluzione

Cosa stai testando?

Ci sono tre possibilità, in cima alla mia testa:

  

A. Stai testando la classe DAO (oggetto di accesso ai dati), assicurandoti che esegua il marshalling corretto dei valori / parametri che vengono passati al database e che i risultati di marshalling / trasformazione / packaging siano ottenuti correttamente dal database.

In questo caso, non è necessario connettersi affatto al database; hai solo bisogno di un unit test che sostituisca il database (o il livello intermedio, ad es., JDBC, (N) Hibernate, iBatis) con una simulazione.

  

B. Stai testando la correttezza sintattica di (generato) SQL.

In questo caso, poiché i dialetti SQL differiscono, si desidera eseguire l'SQL (possibilmente generato) sulla versione corretta del proprio RDBMS, piuttosto che tentare di deridere tutte le stranezze del proprio RDBMS (e in modo che eventuali aggiornamenti RDBMS che cambiano funzionalità sono catturati dai tuoi test).

  

C. Stai testando la semantica correttezza del tuo SQL, ovvero, per un dato set di dati di base, le tue operazioni (accessi / selezioni e mutazioni / inserimenti e aggiornamenti) producono il nuovo set di dati previsto.

Per questo, vuoi usare qualcosa come dbunit (che ti permette di impostare una linea di base e confrontare un set di risultati con un set di risultati atteso), o possibilmente fare i tuoi test interamente nel database, usando la tecnica che ho delineato qui : Il modo migliore per testare le query SQL .

Altri suggerimenti

Questo è il motivo per cui i test unitari (IMHO) possono talvolta creare un falso senso di sicurezza da parte degli sviluppatori. Nella mia esperienza con applicazioni che parlano a un database, gli errori sono comunemente il risultato di uno stato imprevisto dei dati (valori insoliti o mancanti, ecc.). Se ridicolizzi sistematicamente l'accesso ai dati nei test delle unità, penserai che il tuo codice funzioni alla grande quando in realtà è ancora vulnerabile a questo tipo di errore.

Penso che il tuo approccio migliore sia quello di avere un database di test a portata di mano, pieno di goccioline di dati scadenti, ed eseguire i test dei componenti del database su questo. Nel frattempo, ricordando che i tuoi utenti saranno molto meglio di te nel rovinare i tuoi dati.

Il punto centrale di un unit test è testare un'unità (duh) in isolamento. Il punto centrale di una chiamata al database è integrare con un'altra unità (il database). Ergo: non ha senso effettuare unit test delle chiamate al database.

Dovresti, tuttavia, effettuare chiamate al database dei test di integrazione (e, se vuoi, puoi usare gli stessi strumenti che usi per i test unitari).

Per amore di Dio, non eseguire test su un database live, già popolato. Ma lo sapevi.

In generale hai già un'idea del tipo di dati che ogni query recupererà, sia che tu stia eseguendo l'autenticazione degli utenti, cercando le voci della rubrica / organigramma o qualsiasi altra cosa. Sai quali campi ti interessano e sai quali vincoli esistono su di essi (ad es. UNIQUE , NOT NULL e così via). Stai testando l'unità il tuo codice che interagisce con il database, non con il database stesso, quindi pensa in termini di come testare quelle funzioni. Se è possibile che un campo sia NULL , dovresti avere un test che assicuri che il tuo codice gestisca correttamente i valori NULL . Se uno dei tuoi campi è una stringa ( CHAR , VARCHAR , TEXT , e amp; c), verifica per essere sicuro di gestire il escape caratteri correttamente.

Supponiamo che gli utenti tenteranno di inserire qualsiasi cosa * nel database e di generare casi di test di conseguenza. Per questo ti consigliamo di usare oggetti finti.

* Incluso input indesiderato, dannoso o non valido.

Puoi testare tutto tutto tranne: queryDA.Fill(resultSet);

Non appena si esegue queryDA.Fill (resultSet) , è necessario prendere in giro / falsificare il database o eseguire test di integrazione.

Io per primo, non vedo i test di integrazione come cattivi, è solo che catturerà un diverso tipo di bug, ha diverse probabilità di falsi negativi e falsi positivi, non è probabile che venga fatto molto spesso perché è così lento.

Se stavo testando l'unità di questo codice, convaliderei che i parametri siano stati compilati correttamente, il generatore di comandi crea il giusto numero di parametri? Hanno tutti un valore? Null, stringhe vuote e DbNull vengono gestiti correttamente?

La compilazione effettiva del set di dati sta testando il database, che è un componente instabile che esula dall'ambito del DAL.

A rigor di termini, un test che scrive / legge da un database o da un file system non è un test unitario. (Anche se può essere un test di integrazione e può essere scritto usando NUnit o JUnit). I test unitari dovrebbero testare le operazioni di una singola classe, isolandone le dipendenze. Pertanto, quando si scrive unit test per l'interfaccia e i livelli di logica aziendale, non è necessario disporre di un database.

OK, ma come si esegue il test unitario del livello di accesso al database? Mi piace il consiglio di questo libro: xUnit Test Patterns (il link punta al libro "Testing w / DB" capitolo. Le chiavi sono:

  • utilizza i test di andata e ritorno
  • non scrivere troppi test nel dispositivo di test di accesso ai dati, poiché verranno eseguiti molto più lentamente del tuo "reale" unit test
  • se riesci ad evitare i test con un database reale, esegui un test senza un database

Per i test unitari di solito derido o falso il database. Quindi utilizzare l'implementazione fittizia o falsa tramite iniezione di dipendenza per testare il metodo. Probabilmente avresti anche dei test di integrazione che metteranno alla prova vincoli, relazioni con le chiavi esterne, ecc. Nel tuo database.

Per quanto riguarda ciò che testeresti, ti assicureresti che il metodo stia utilizzando la connessione dai parametri, che la stringa di query sia assegnata al comando e che il tuo set di risultati restituito sia lo stesso che stai fornendo tramite un'aspettativa sul metodo Fill. Nota: è probabilmente più semplice testare un metodo Get che restituisce un valore rispetto a un metodo Fill e modifica un parametro.

Per farlo correttamente, dovresti usare un po 'di dipendenza (DI), e per .NET ce ne sono diversi. Attualmente sto usando Unity Framework ma ce ne sono altri che sono più facili.

Ecco un link da questo sito su questo argomento, ma ce ne sono altri: Iniezione delle dipendenze in .NET con esempi?

Ciò ti consentirebbe di deridere più facilmente altre parti della tua applicazione, semplicemente avendo una classe finta implementa l'interfaccia, in modo da poter controllare come risponderà. Ma questo significa anche progettare un'interfaccia.

Dato che hai chiesto delle migliori pratiche, questa sarebbe una, IMO.

Quindi, non andare nel db a meno che non sia necessario, come suggerito è un altro.

Se è necessario testare determinati comportamenti, come le relazioni con le chiavi esterne con l'eliminazione a cascata, è possibile scrivere test di database per questo, ma in genere non è meglio andare a un database reale, specialmente perché più di una persona può eseguire un unit test alla volta e se stanno andando allo stesso database i test potrebbero non riuscire poiché i dati previsti potrebbero cambiare.

Modifica: per unità di test del database intendo questo, poiché è progettato per utilizzare solo t-sql per eseguire alcune impostazioni, test e smontaggi. http://msdn.microsoft.com/en- us / library / aa833233% 28VS.80% 29.aspx

Nel progetto basato su JDBC, la connessione JDBC può essere derisa, in modo che i test possano essere eseguiti senza RDBMS attivo, con ogni caso di test isolato (nessun conflitto di dati).

Permette di verificare, il codice di persistenza passa query / parametri corretti (ad es. https://github.com/playframework/playframework/blob/master/framework/src/anorm/src/test/scala/anorm/ParameterSpec.scala ) e gestire i risultati JDBC (analisi / mapping) come previsto ("contiene tutti i bit e i pezzi necessari per estrarre alcuni dati dal database e restituisce i dati in un oggetto DataTable").

Framework come jOOQ o my framework Acolyte possono essere utilizzati per: https://github.com/cchantep/acolyte .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top