2010-02-11 2 views
6

Ho una classe che restituisce un semplice file di report. Legge alcuni numeri ID record da un file XML: ognuno utilizzato per trovare un record corrispondente memorizzato in un database. Quindi scrive i dettagli di ogni record in un file CSV.Unità di test di una classe che utilizza il file system

Mi chiedo: qual è il modo migliore per organizzarlo in modo che sia facile da testare, ma segue i principi dell'incapsulamento? Capisco che è meglio evitare di interagire con il file system a meno che non sia assolutamente necessario, quindi mi occupo di oggetti Stream. Quando collaudo unità, posso usare oggetti simulati parziali per sovrascrivere i bit che leggono o scrivono sui file.

Sono anche sicuro di quando/dove disporre i flussi senza rendere difficile il test dell'unità. Sembra che potrei dover esporre i flussi al test unitario.

Il mio progetto utilizza NHibernate per l'accesso ai dati, Spring .NET per l'iniezione delle dipendenze e Rhino.Mocks per il test delle unità.

Attualmente ho qualcosa di simile a questo:

public class ReportBiz 
{ 
    //Access to repository, populated by Spring 
    internal ICardRequestDAO CardRequestData { get;set;} 

    //This normally returns a FileStream containing data from the XML file. When testing this is overridden by using a Rhino.Mocks partial mock and returns a MemoryStream 
    internal virtual Stream GetSourceStream() 
    { 
     //Load file and return a Stream 
     ... 
    } 

    //This normally returns a FileStream where CSV data is saved. When testing this is overridden by using a Rhino.Mocks partial mock and returns a MemoryStream 
    internal virtual Stream GetOutputStream() 
    { 
     //Create file where CSV data gets saved and return a Stream 
     ... 
    } 

    public void CreateReportFile() 
    { 
     Stream sourceStream = GetSourceStream(); 
     ... 

     //Create an XmlDocument instance using the stream 
     //For each XML element, get the corresponding record from the database 
     //Add record data to CSV stream  
     ... 
    } 
    } 

sarebbe meglio utilizzare un qualche tipo di fabbrica su misura o qualcosa e passare i corsi d'acqua nel costruttore? Ma che cosa succede se c'è qualche logica aziendale, ad es. il nome del file viene determinato in base ai risultati di una query?

Oppure l'intero problema dell'accesso ai file non è un problema?

Mi scuso se mi manca qualcosa di ovvio. Sarei grato per qualsiasi consiglio.

+0

Ho bisogno di chiedere: cosa fa il tuo XML? Contiene solo un elenco di chiavi che puntano a un record del database? –

+0

Oltre alla chiave del database, contiene altri tre campi necessari per il record. Anche questi vengono utilizzati nella relazione. – James

+0

Correlati: http://stackoverflow.com/questions/129036/unit-testing-code-with-a-file-system-dependency –

risposta

8

Il modo più semplice fare l'accesso ai file mock-grado, pur mantenendo il controllo sul ciclo di vita delle risorse disponibili è quello di iniettare un StreamFactory nella tua classe:

public class ReportBiz { 

    private IStreamFactory streamFactory; 

    public ReportBiz(IStreamFactory streamFactory) { 
     this.streamFactory = streamFactory 
    } 

    public void CreateReportFile() { 
     using(Stream stream = this.streamFactory.CreateStream()) { 
      // perform the important work! 
     } 
    } 
} 

quando non c'è più la logica di business coinvolti, il metodo factory può essere un po 'più elaborato, ma non di molto:

public void CreateReportFile() { 
    string sourcePath = this.otherComponent.GetReportPath(); 
    using(Stream stream = this.streamFactory.CreateStream(sourcePath)) { 
     // perform the important work! 
    } 
} 
+0

Grazie per la risposta. Ma sono ancora un po 'confuso su come verificare quindi lo stream del file di report CSV per assicurarmi di aver scritto i dati corretti. Se il flusso di output fa parte di un'istruzione using, non posso verificarlo. Dovrei semplicemente mantenere un riferimento ad esso a livello di classe ed evitare di eliminarlo fino a quando l'istanza di ReportBiz stessa non viene eliminata? – James

+0

Non ho usato Rhino Mocks, ma in NMock, avrei iniettato un simulato 'IStreamFactory', ho stubato la chiamata al suo metodo' CreateStream' e gli ho restituito un mock Stream su cui avrei impostato delle aspettative (per le chiamate a 'Stream.Write') per verificare che ho scritto i dati corretti. –

2

dovrete prendere in giro i flussi in qualche modo per evitare di esporli alla prova, perché la carne della vostra logica di business è sempre la stringa di input e la uscita st squilla correttamente.

l'argomento decisivo in questo scenario è che devi riconoscere che hai tre fasi nel vostro flusso di dati:

  • la lettura dei dati: analisi dei dati è un unico, distinto preoccupazione
  • Il contenuto dell'output: devi verificare che dati dati corretti, hai l'output di stringa CSV corretto
  • Scrivere quel contenuto nel file

Onestamente, l'intero problema di scrittura dei file non è un grosso problema: .NET Framework offre funzionalità di scrittura dei file molto ben testate e va oltre lo scopo dei test. La maggior parte dei problemi che incontrerai sarà l'accuratezza del CSV che sputi ai file.

Come nota precauzionale, vorrei sconsigliare di far girare il proprio autore CSV.Dovresti provare a trovare le librerie CSV già esistenti: there are a lot of them over the net.