2012-03-02 15 views
6

Sono una codifica per sviluppatori .Net in C#, e sono stato assegnato a un progetto che il suo manager vuole che venga accuratamente testato unitamente, per lo più sottolineando l'isolamento in modo che un errore logico fallisca idealmente uno test, e non ci sono dipendenze tra i test.Test unitario Aggiungi/ottieni coppie di metodi

Oggi stiamo discutendo modelli di test, e la seguente questione è giunto il momento:

Diciamo che abbiamo un oggetto denominato MyHashTable, che implementa:

void Add(string key, string value); 

string GetValue(string key); 

vogliamo testare ciascuno di questi metodi indipendentemente. Il problema principale ovviamente, logicamente non possiamo ottenere ciò che non abbiamo mai aggiunto e non possiamo verificare che qualcosa sia stato aggiunto senza averlo ottenuto. Abbiamo sentito e letto di stub/mocking e altre tecniche che potrebbero aiutare a superare questi problemi, ma non possiamo decidere quale soluzione sarebbe più leggibile e controllabile.

Pertanto, chiedo consigli/idee su come testare questi metodi da soli e, se è possibile, includere pro e contro per il suggerimento. Grazie!

+0

Si prega di chiarire, è MyHashTable un oggetto di un tipo che ti implementato e vuole testare la struttura interna di, o solo un'istanza di una classe che si sta utilizzando "come è" e vuole testare l'interfaccia esterna del? –

risposta

2

Se si desidera ottenere la completa indipendenza e isolamento , è probabile che sia necessario esporre alcuni interni di MyHashTable. Ad esempio, alla base collezione che Add aggiunge elementi a:

public class MyHashTable<TKey, TValue> 
{ 
    internal Dictionary<TKey, TValue> Storage { get; set; } 
    // ... 
} 

Questo non è molto carina come si può vedere, ma come ho detto, non è possibile testare i due metodi in un completo isolamento, senza esporre qualcosa (come hai notato, è un metodo coppia). Puoi naturalmente fornire altri modi per inizializzare la tua classe, ad es. con il costruttore che prende la raccolta, ma solo delega un ulteriore problema - devi verificare che anche la funzione di costruzione abbia funzionato, e probabilmente avrai bisogno del metodo Get per ... che ti riporterà al problema originale.

Edit:

Si noti che non sto suggerendo che rivelano internals/dettagli di implementazione come l'unica strada da seguire. È possibile semplicemente testare questi metodi insieme (non come nel test singolo, ma in un altro), che potrebbe rivelarsi una soluzione migliore. Certo - se il tuo Add fallisce, anche i test Get falliranno. Ma poi di nuovo, vuoi riparare rotto Add - una volta che tutto è tornato alla normalità. Il problema risiede naturalmente nel modo in cui si distingue se fosse Add o Get che si è rotto - ma è qui che i messaggi di errore di asserzione o il concetto di asserzioni di protezione che ho collegato nel commento sono utili.

La verità è che a volte la separazione completa e l'isolamento sono troppo difficili da raggiungere o semplicemente avrebbero bisogno di introdurre un design scadente. Questo è uno dei motivi per cui non dovrebbero mai essere l'obiettivo finale.

+0

Quindi fondamentalmente questi metodi devono essere trattati come una coppia e non possono essere testati separatamente l'uno dall'altro. Nei tuoi test, i progetti hanno consentito a un gruppo di test di dipendere l'uno dall'altro e di non essere completamente isolati? Non è necessario fissare il concetto di isolamento, se significa iniziare ad aggiungere interni interni o altre cose che renderanno il codice sotto test meno controllabile – Eldar

+0

@ user1244563: Il problema qui è che alcune cose non possono essere testate in completo isolamento (il tuo 'Add' /' Get' è un esempio perfetto), a meno che non esponga alcune informazioni extra sul loro funzionamento interno (in questo caso - raccolta su cui lavorano). Direi che è abbastanza normale presumere che uno di loro funzioni ad un certo punto e se ne possa fidare più tardi (penso che potrebbe interessarti controllare [questi] (http://stackoverflow.com/q/9018142/343266) [due ] (http://stackoverflow.com/q/9385715/343266) domande). –

+0

Rivelare il funzionamento interno è qualcosa con cui non sono assolutamente d'accordo ... non perché sono un maniaco dell'incapsulamento (tanto quanto non sono un maniaco dell'isolamento) ma perché un giorno potrei voler usare, diciamo, MemCache come mio immagazzinamento interno. Quindi ora tutti i miei test devono essere sottoposti a refactoring. Non voglio test basati su operazioni interne nello stesso modo in cui non voglio che i miei client API vengano inoltrati su di essi (un test è in realtà solo un altro client API). Lo faccio solo quando si prevede che il codice chiami un altro codice (test di interazione) perché voglio garantire la separazione delle preoccupazioni, per garantire che venga utilizzata un'API quando necessario e nient'altro. – Eldar

0

Perché? Lo scopo di un test unitario è assicurarsi che il metodo testato funzioni come previsto. Nulla dice che non è possibile utilizzare gli altri metodi per assicurarsi che il metodo testato funzioni correttamente.

i seguenti test sono per me va bene (per un'implementazione tabella hash)

[Fact] 
public void AddNewItem() 
{ 
    var hashTable = new MyHashTable(); 

    hashTable.Add("hello", "world"); 

    Assert.True(hashTable.Exists("hello")); 
    Assert.Equal("world", hashTable["hello"]); 
} 

[Fact] 
public void AddExistingItem() 
{ 
    var hashTable = new MyHashTable(); 

    hashTable.Add("hello", "world"); 
    hashTable.Add("hello", "realItem"); 

    Assert.Equal("realItem", hashTable["hello"]); 
} 

Si potrebbe, naturalmente, rompere l'archiviazione interna in un'interfaccia ed iniettarlo in un costruttore:

public class MyHashTable 
(
    public MyHashTable(IKeyValueStorage storage) 
    {} 

    // creates a default storage class 
    public MyHashTable() 
    {} 
) 

Che ti permette di prendere in giro lo storage e quindi testare un singolo metodo alla volta. Ma ciò sposta il problema un ulteriore passo avanti. come hai intenzione di testare l'implementazione dello storage?

Quello che sto dicendo è che una HashTable è una piccola unità isolata. Va bene testarlo usando i suoi metodi. Non avrai questo problema con i modelli di dominio dato che puoi prendere in giro le loro dipendenze.