Milan Nankov – Blog

Mocking The File System to Improve Testability 2009/12/10

Recently I started writing a simple application that would help me organize my music files by automatically renaming them using their tags and moving them to their designated folders.

As expected the program will rely on the file system classes that the .Net Framework provides. Naturally, I wanted to cover the I/O logic with tests so that I am confident that when the software is used it will not do damage to my files and will behave according to my requirements. So I started writing tests…

And here is the catch – in general it is not a good idea to have tests that are performing I/O operations like accessing files and databases. In such situations, but of course not limited to, mock objects are your friend. And before I go into more details about what mocking really is lets find out why mocking I/O is important? Well, for a ton of reasons:

  • Usually faster than performing I/O operations
  • You do not have have to deal with security issues
  • You have more control – easily test intricate scenarios
  • Test setup is far more easier to do 

The first thing that most people think about when asked about mocking is interfaces. And that is no surprise since mocking is built around interfaces. Here is a simplified version of the file system interface that I have created for my application:

This interface represents all operations that my application will be able to perform on the file system. As we will see, this interface allows me to create a fake implementation of the file system functionality (mock) which will be the cornerstone of all of my I/O tests.

As you have probably guessed I will be using MSTest to drive my tests but you can use a testing framework of your choice. In addition to a testing framework you also need a mocking framework. My favorite one is called Moq – it is very powerful and yet easy to use.

Let’s write some tests…

The first thing to do is to create the mock implementation of the interface that we are interested in. This first line of the Initializer  method will create a dummy implementation (mock) of the specified interface (in this case IFileSystem) and you can use the Object property to interact with an instance of the this dummy interface implementation. For convenience we copy the instance reference to “this.fileSystem”.

The mock (“this.fileSystemMock”) allows you to control the behavior of the mocked object and setup assertions which allow you to test the behavior of the mocked object.

The following two tests will assure that the Rename method of the MusicFileConfigurator class behaves according to my specifications. Here is the code that will be tested:

Notice that the constructor accepts a parameter of type IFileSystem. This is very important, as you will see shortly, since it enables us to plug in different interface implementations. This technique is know as Dependency Injection – in this particular case we use the so called Constructor injection. For the sake of simplicity the Rename method will always rename the specified file to “renamed.test.file”.

Back to the tests… First I want to be sure that if a non-existent file is passed as argument the method will throw an exception.

The first line, though boring, is crucial for understanding how the test works. Remember that “this.fileSystem” is actually an instance of the mock implementation? Well, now the MusicFileConfigurator is going to use this mock implementation of the IFileSystem.

The second line of the test reveals some of the power of Moq and mocking in general. This line instructs the file system mock to return “false” whenever “test.file” is specified as argument to the FileExists method. By doing that we ensure that the first condition of Rename will not be met and FileNotFoundException will be thrown.

The Setup method is very powerful and allows you to specify how the methods and the properties of your mocked objects behave – what results they returns, whether they throw exceptions and more.

Moving on to a move sophisticated test which asserts that the Rename method can successfully rename the specified file.

Once again we have almost the same setup with just two additional lines. This time we want to test that when the Rename method executes without any errors it will actually rename the specified file. The two Setup statements ensure that the specified file exists and that the new filename does not (which is required by the Rename method).

So far we have just ensured that the Rename method will run without errors. To be sure that it can really rename files we have to make sure that Rename will call MoveFile with the correct arguments. With Moq this is a piece of cake.

Whenever we want to make sure that certain method has been executed or hasn’t been executed we use the Verify method. In this particular setup I want to assert that the Rename method calls the MoveFile method of the file system exactly one time with the expected arguments. If the conditions are not met – for instance the method is called more times – Verify will throw exception and the test will fail. 

I do not know if it is just me but I think that this piece of test code is simply beautiful. In addition to the easily readable fluent interface of Moq you are not dealing with the actual file system which spares you a a great deal of time and resources to setup our tests.

You might be wondering how is the MusicFileConfigurator going to working in a real scenario? Well, I have created a special implementation of the IFileSystem interface which uses the actual file system.

And whenever I initialize the MusicFileConfigurator I do something like that:

I have attached the full source code for this article which includes more tests.

Related links:
The Moq Project
Beginning Mocking with Moq

Download File – FileSystemMockingSource