I have a class that outputs a simple report file. It reads some record ID numbers from an XML file: each one used for finding a matching record stored in a database. It then writes out each record's details to a CSV file.
I am wondering - what is the best way to organise it so that it is easy to test, yet follows the principles of encapsulation? I gather that it is best to avoid interacting with the file system unless absolutely necessary so I am dealing with Stream objects. When unit testing I can use partial mock objects to override the bits that read or write to files.
I am also unsure of when/where to dispose the streams without making unit testing tricky. It looks like I might have to expose the streams to the unit test.
My project uses NHibernate for data access, Spring .NET for dependency injection, and Rhino.Mocks for unit testing.
Currently I have something similar to this :
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
...
}
}
Would it be better to use some kind of custom factory or something and pass the streams into the constructor? But what about if there is some business logic involved, e.g. the filename is determined based on results of a query?
Or is the whole file access thing not a problem after all?
Apologies if I'm missing something obvious. I would be grateful for any advice.