Tuesday, February 15, 2011

Unit testing a method that makes heavy use of FileStream (C#)

While working on the HTML Combiner, I ran into a situation where I was trying to test a method that wrote a configuration file. This method makes heavy use of FileStream, and so the resulting test would have been coupled to the filesystem. This seems like a common issue you would run into, and there are a few ways to approach the unit test for such a method:
  1. Allow the filesystem dependency
  2. Create an "IDataSource" interface and make an implementation that wraps the FileStream object
  3. Instead of using FileStream, use its parent, Stream, in the method you're trying to test
Option (1) is out since that's exactly what we're trying to avoid! Allowing the dependency on the file system is a bad idea, because it allows anything that can go wrong with file IO to potentially fail your unit test at an unpredictable time. In addition, if you're writing to files, you have to worry about cleaning up so your tests don't read the file of a previous test run.

Option (2) seems like a good idea because you can stub the IDataSource interface in the unit test. In fact, I probably will do this later on (I admit, I was rushed for time!). This means that you will have to change your method's code to use the interface instead of changing "FileStream" to "Stream," but it's a more elegant solution that does not depend on Stream at all.

Option (3) is the one I went with, and isn't a terrible solution. By using a Stream object in the target method, you no longer depend on the FileStream (and consequently the file system). In your test for example, you can pass in a MemoryStream which contains a test "file" whose contents are in a string you declare. However, I did encounter some disadvantages:
  • You still have a dependency on Stream, instead of using a stub
  • You have to specify the encoding your string uses in order to create a MemoryStream out of it
  • Creating and writing to the MemoryStream is not painless
Well, we still achieve decoupling from the filesystem, which was the main goal. However, in the future, I will probably switch to an "IDataSource" interface and use that. This way, we get total independence from Stream and we could also write an implementation that, say, reads/writes an SQL database.

No comments:

Post a Comment