Domanda

Ad esempio, sto scrivendo test su un CsvReader. È una classe semplice che enumera e divide le righe di testo. La sua unica ragion d'essere sta ignorando le virgole tra virgolette. È meno di una pagina.

Per " scatola nera " testando la classe, ho controllato cose come

  • Cosa succede se il file non esiste?
  • Cosa succede se non ho i permessi per il file?
  • Cosa succede se il file ha interruzioni di riga non Windows?

Ma in realtà, tutte queste cose sono affari di StreamReader. La mia classe funziona senza fare nulla su questi casi. Quindi, in sostanza, i miei test stanno rilevando errori generati da StreamReader e testando il comportamento gestito dal framework. Sembra un sacco di lavoro per niente.

Ho visto le domande correlate

La mia domanda è: mi manca il punto di "scatola di vetro"? test se uso ciò che so per evitare questo tipo di lavoro?

È stato utile?

Soluzione

Questo dipende in realtà dall'interfaccia del tuo CsvReader, devi considerare cosa si aspetta l'utente della classe.

Ad esempio, se uno dei parametri è un nome file e il file non esiste cosa dovrebbe accadere? Questo non dovrebbe dipendere dal fatto che tu usi o meno un lettore di stream. I test unitari dovrebbero testare il comportamento esterno osservabile della tua classe e, in alcuni casi, approfondire leggermente e garantire inoltre che siano coperti alcuni dettagli di implementazione, ad es. il file viene chiuso al termine del lettore.

Tuttavia, non si desidera che i test unitari dipendano da tutti i dettagli o si supponga che a causa di un dettaglio dell'implementazione accadrà qualcosa.

Tutti gli esempi che menzioni nella tua domanda riguardano comportamenti osservabili (in questo caso circostanze eccezionali) della tua classe e quindi dovrebbero avere unit test relativi a loro.

Altri suggerimenti

Non credo che dovresti perdere tempo a testare cose che non sono il tuo codice. È una scelta di progettazione, non una scelta di prova, se gestire gli errori del framework sottostante o lasciarli propagare fino al chiamante. FWIW, penso che tu abbia ragione a lasciarli propagare. Una volta presa la decisione di progettazione, tuttavia, i test delle unità dovrebbero coprire il codice (e coprirlo bene) senza testare il framework sottostante. Usare iniezione di dipendenza e probabilmente un finto Stream è probabilmente una buona idea.

[EDIT] Esempio di iniezione di dipendenza (vedi link sopra per maggiori informazioni)

Non usando l'iniezione delle dipendenze abbiamo:

public class CvsReader {
   private string filename;
   public CvsReader(string filename)
   {
      this.filename = filename;
   }

   public string Read()
   {
      StreamReader reader = new StreamReader( this.filename );
      string contents = reader.ReadToEnd();
      .... do some stuff with contents...
      return contents;
   }
}

Con l'iniezione delle dipendenze (iniezione del costruttore) facciamo:

public class CvsReader {
   private IStream stream;
   public CvsReader( IStream stream )
   {
      this.stream = stream;
   }

   public string Read()
   {
       StreamReader reader = new StreamReader( this.stream );
       string contents = reader.ReadToEnd();
       ...  do some stuff with contents ...
       return contents;
   }
}

Ciò consente a CvsReader di essere più facilmente testabile. Passiamo un'istanza che implementa l'interfaccia da cui dipendiamo nel costruttore, in questo caso IStream. Per questo motivo possiamo creare un'altra classe (forse una classe simulata) che implementa IStream, ma non necessariamente esegue l'I / O dei file. Possiamo usare questa classe per fornire al nostro lettore tutti i dati che vogliamo senza coinvolgere il framework sottostante. In questo caso, userei un MemoryStream dato che ne abbiamo appena letto. Volevamo, tuttavia, utilizzare una classe simulata e darle un'interfaccia più ricca che consenta ai nostri test di configurare le risposte che fornisce. In questo modo possiamo testare il codice che scriviamo e non coinvolgere affatto il codice del framework sottostante. In alternativa, potremmo anche passare un TextReader, ma il modello di iniezione di dipendenza normale utilizza interfacce e volevo mostrare il modello con interfacce. Probabilmente passare in un TextReader sarebbe meglio poiché il codice sopra dipende ancora dall'implementazione di StreamReader.

Sì, ma sarebbe strettamente ai fini del test unitario:

Potresti sottrarre l'implementazione del tuo lettore CSV da qualsiasi StreamReader specifico definendo un'interfaccia del lettore di stream astratta e testando la tua implementazione con un lettore di stream simulato che implementa tale interfaccia. Il tuo finto lettore sarebbe ovviamente immune da errori come file inesistenti, problemi di autorizzazione, differenze del sistema operativo ecc. Pertanto, verrai testato interamente il tuo codice e otterrai una copertura del codice del 100%.

Tendo ad essere d'accordo con tvanfosson: se erediti da uno StreamReader e lo estendi in qualche modo, i tuoi test unitari dovrebbero esercitare solo la funzionalità che hai aggiunto o modificato. Altrimenti, perderai molto tempo ed energia cognitiva scrivendo, leggendo e mantenendo test che non aggiungono alcun valore.

Sebbene markj abbia ragione, i test dovrebbero coprire il "comportamento esterno osservabile". di una classe, penso che sia appropriato considerare da dove il comportamento. Se si tratta di un comportamento tramite ereditarietà di un'altra classe (presumibilmente unit test), non vedo alcun vantaggio nell'aggiungere i tuoi test unità . OTOH, se si tratta di un comportamento attraverso la composizione, potrebbe giustificare alcuni test per garantire che i pass-through funzionino correttamente.

La mia preferenza sarebbe quella di testare l'unità delle funzionalità specifiche che modificate, quindi scrivere test di integrazione per verificare le condizioni di errore, ma nel contesto delle esigenze aziendali che in definitiva state supportando.

Solo un FYI, se questo è .NET, dovresti considerare di non reinventare la ruota.

Per C #

Aggiungi un riferimento a Microsoft.VisualBasic Usa la fantastica classe Microsoft.VisualBasic.FileIO.TextFieldParser () per gestire le tue esigenze di analisi CSV.

Microsoft l'ha già testato, quindi non sarà necessario.

Enjoy.

Dovresti sempre gestire gli errori generati dal tuo framework; in questo modo la tua applicazione è robusta & amp; non si blocca su errori catastrofici ...

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