2010-02-11 6 views
6

Mam klasę, która wyprowadza prosty plik raportu. Odczytuje niektóre numery rekordów z pliku XML: każdy używany do znalezienia pasującego rekordu przechowywanego w bazie danych. Następnie zapisuje dane każdego rekordu w pliku CSV.Jednostka testująca klasę korzystającą z systemu plików

Zastanawiam się - jaki jest najlepszy sposób zorganizowania go tak, aby był łatwy do przetestowania, a jednocześnie przestrzegał zasad enkapsulacji? Uważam, że najlepiej jest unikać interakcji z systemem plików, chyba że jest to absolutnie konieczne, więc mam do czynienia z obiektami Stream. Podczas testowania jednostkowego mogę używać częściowych próbnych obiektów, aby nadpisać bity, które odczytują lub zapisują do plików.

Nie jestem również pewien, kiedy i gdzie wyrzucać strumienie bez konieczności wykonywania testów jednostkowych. Wygląda na to, że muszę wystawić strumienie na test jednostkowy.

Mój projekt wykorzystuje NHibernate do dostępu do danych, Spring .NET do iniekcji zależności i Rhino.Mocks do testowania jednostkowego.

Obecnie mam coś podobnego do tego:

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  
     ... 
    } 
    } 

Byłoby lepiej użyć jakiegoś niestandardowego fabryce czy coś i przekazać strumienie do konstruktora? Ale jeśli chodzi o logikę biznesową, np. nazwa pliku jest określana na podstawie wyników zapytania?

Czy mimo wszystko nie ma problemu z dostępem do plików?

Przepraszam, jeśli brakuje mi czegoś oczywistego. Byłbym wdzięczny za każdą radę.

+0

Muszę zapytać: co robi twój XML? Czy zawiera tylko listę kluczy wskazujących na rekord bazy danych? –

+0

Oprócz klucza bazy danych zawiera również trzy inne pola, które muszę powiązać z rekordem. Te również zostaną wykorzystane w raporcie. – James

+0

Powiązane: http://stackoverflow.com/questions/129036/unit-testing-code---file-system-dependency –

Odpowiedz

8

Najprościej zrobić dostęp do plików mock-stanie przy jednoczesnym zachowaniu kontroli nad cyklem życia zasobów jednorazowych jest wstrzyknąć StreamFactory w swojej klasie:

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! 
     } 
    } 
} 

gdy istnieje więcej logiki biznesowej zaangażowany, metoda fabryki może być nieco bardziej skomplikowane, ale nie za dużo:

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

Dzięki za odpowiedź. Ale wciąż jestem nieco zdezorientowany, w jaki sposób następnie weryfikuję strumień plików raportu CSV, aby upewnić się, że zapisałem poprawne dane. Jeśli strumień wyjściowy jest częścią instrukcji using, nie mogę go przetestować. Czy powinienem po prostu odwołać się do niego na poziomie klasy i unikać jego wyrzucania, dopóki sama instancja ReportBiz nie zostanie usunięta? – James

+0

Nie użyłem Rhino Mocks, ale w NMock, wstrzyknę fałszywą 'IStreamFactory', wywołaj metodę" CreateStream "i zwrócę fałszywy Stream, na którym ustawiłbym oczekiwania (w przypadku wywołań do 'Stream.Write') w celu sprawdzenia, czy zapisałem poprawne dane. –

2

Musisz mock swoje transmisje jakoś unikać wystawiania ich na próbę, bo mięso logiki biznesowej jest coraz ciąg wejściowy i wyjście st dzwoni poprawnie.

Clincher w tym scenariuszu to trzeba przyznać, że masz trzy etapy w swoim strumieniu danych:

  • odczyt danych: analizowania danych jest unikalny, odrębny dotyczą
  • The zawartość wyjścia:: musisz sprawdzić, czy podano poprawne dane, masz prawidłowy wynik ciągu CSV:
  • Zapisanie tej zawartości do pliku

Szczerze mówiąc, cały problem z pisaniem plików nie stanowi większego problemu - system .NET Framework oferuje całkiem dobrze przetestowane funkcje tworzenia plików i wykracza poza zakres testów. Większość problemów, które napotkasz, będzie dokładnością CSV, którą wyplułeś do plików.

Odwołując się od ostrożności, odradzam samodzielne tworzenie własnych skryptów CSV.Powinieneś spróbować znaleźć biblioteki CSV, które już istnieją - there are a lot of them over the net.