Many applications require access to the file system to create, modify or delete files and folders. But how do you make sure that such application behaves correctly? You do it with tests of course but there is a catch: In general it is not a good idea to have tests that are performing Input/Output operations like accessing files and databases. When you need to test I/O operations mock objects are your friend. And before I go into more details let me point out some of the benefits of mocking.
Let’s start with a very simple class called FileManager which can perform a single operation called Rename. I want to make sure that Rename will correctly rename a specified file. For the sake of simplicity Rename will always rename the specified file to “ranamed.file”. Here is the code of FileManager:
By taking a quick look at Rename you will notice that it has the following behavior which has to be tested:
So how do we go about testing this behavior? If you take a look at the constructor of FileManager you will notice that it accepts a single argument of type IFileSystem. This interface defines all operations that can be perform on the file system. All I/O operations that FileManager performs are executed through this interface.
As we will see, IFileSystem allows me to create a fake implementation of the file system functionality (mock) which will be the cornerstone of my I/O tests.
I will be using MSTest to drive my tests but you can use a testing framework of your choice. Although you are free to create mocks by hand you will be better off using a mocking framework. I have chosen the new, but yet powerful, kid on the block - JustMock. Armed with JustMock you will be able to easily mock interfaces and classes. In this particular case I will use JustMock to mock the IFileSystem interface.
Let’s write some tests.
I use the Initialize method to create a mock implementation of IFileSystem and use it to initialize the FileManager that will be tested.
Assert that exception is thrown when non-existent file is passed to Rename
The magic here happens on the first line of the test where we call Mock.Arrange. This call makes sure that when FileExists is called with “file.to.rename” argument it will return false. After that we simply call Rename and if FileNotFoundException is raised than we have a correct behavior.
Assert that exception is thrown when new filename already exists
As you can see this test is very similar to the first one. The difference is that we first have to ensure that FileExists(“file.to.rename”) returns true – this makes sure that the fist condition of the Rename method is met (input file exists). Since we know that Rename will always rename the input file to “renamed.file” we instruct the file system mock to return true for FileExists(“renamed.file”). This way we simulate the case where the file we want to rename exists but the new filename already exists on disk. The last step is to actually call Rename.
Assert that file is actually renamed
Finally we have to make sure that file is actually renamed when no exception are encountered.
This test is a very good demonstration of the Arrange Act Assert pattern of writing tests. First we setup the preconditions of the test – in this case we ensure that FileExists(“file.to.rename”) returns true and FileExists(“renamed.file”) returns false. After that we call the method that we are testing. Finally we assert that our expectations are correct. In this particular case we assert that Rename calls MoveFile where the first argument is the old filename (“file.to.rename”) and the second one is the new filename (“renamed.file”).
What do you think about those tests?
I believe that they are easy to read and modify. Moreover they are all short which is always a good property of a test.
There is one more thing. How do we use FileRenamer in a real application? Well, we first create an implementation of IFileSystem that will actually invoke the file system.
And finally we create a FileManager with DefaultFileSystem.
You can find the code used for the examples in this github repo.
Mihail Vladov is a Software Engineering Manager at Progress. He has more than a decade of experience with software and product development and is passionate about good software design and quality code. Mihail helped develop the WPF controls suite and Document Processing libraries which are used by thousands of developers. Currently, he is leading the JustMock team. In his free time, he loves to travel and taste different foods. You can find Mihail on LinkedIn.
Subscribe to be the first to get our expert-written articles and tutorials for developers!