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:
public class FileManager
private IFileSystem fileSystem;
public FileManager(IFileSystem fileSystem)
if (fileSystem == null)
throw new ArgumentNullException("fileSystem");
this.fileSystem = fileSystem;
public void Rename(string filePath)
throw new FileNotFoundException("File was not found", filePath);
var renamedFilePath = "renamed.file";
throw new IOException("New filename already exists: " + renamedFilePath);
private bool FileDoesNotExist(string filePath)
private bool FileExists(string filePath)
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.
public interface IFileSystem
bool FileExists(string path);
void MoveFile(string filePath, string newFilePath);
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.
public class RenameTests
private FileManager fileManager;
public void Initialize()
this.fileSystem = Mock.Create<IFileSystem>();
this.fileManager = new FileManager(this.fileSystem);
// tests ...
Assert that exception is thrown when non-existent file is passed to Rename
public void Rename_Throws_WhenTheSpecifiedFileDoesNotExist()
Mock.Arrange(() => this.fileSystem.FileExists("file.to.rename")).Returns(false);
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
public void Rename_Throws_WhenTheTheNewFileNameExists()
Mock.Arrange(() => this.fileSystem.FileExists("file.to.rename")).Returns(true);
Mock.Arrange(() => this.fileSystem.FileExists("renamed.file")).Returns(true);
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
public void Rename_RenamesTheInputFile_WhenInputExistsAndNewFileNameDoesNotExist()
Mock.Arrange(() => this.fileSystem.FileExists("renamed.file")).Returns(false);
Mock.Assert(() => this.fileSystem.MoveFile("file.to.rename", "renamed.file"), Occurs.Once());
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.
public sealed class DefaultFileSystem : IFileSystem
public bool FileExists(string path)
public void MoveFile(string filePath, string newFilePath)
And finally we create a FileManager with DefaultFileSystem.
static void Main(string args)
FileManager manager = new FileManager(new DefaultFileSystem());
You can download the source code from here (VS2010 & VS2008)
Copyright © 2017, Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
Progress, Telerik, and certain product names used herein are trademarks or registered trademarks of Progress Software Corporation and/or one of its subsidiaries or affiliates in the U.S. and/or other countries. See Trademarks or appropriate markings.