How to Mock Out the File System in C# for Unit Testing

How do you mock out the file system in C# for unit testing?

Edit: Install the NuGet package System.IO.Abstractions.

This package did not exist when this answer was originally accepted. The original answer is provided for historical context below:

You could do it by creating an interface:

interface IFileSystem {
bool FileExists(string fileName);
DateTime GetCreationDate(string fileName);
}

and creating a 'real' implementation which uses
System.IO.File.Exists() etc. You can then mock this interface using a
mocking framework; I recommend Moq.

Edit: somebody's done this and kindly posted it online here.

I've used this approach to mock out DateTime.UtcNow in an IClock
interface (really really useful for our testing to be able to control
the flow of time!), and more traditionally, an ISqlDataAccess
interface.

Another approach might be to use TypeMock, this allows you to
intercept calls to classes and stub them out. This does however cost
money, and would need to be installed on your whole team's PCs and
your build server in order to run, also, it apparently won't work for
the System.IO.File, as it can't stub mscorlib.

You could also just accept that certain methods are not unit testable
and test them in a separate slow-running integration/system tests
suite.

How do I mock Directory.GetFiles?

You would need to depend on an abstraction to get your files instead of having a hard dependency on System.IO.Directory:

public interface IFileProvider
{
string[] GetFiles(string path);
}

public class PhysicalFileProvider : IFileProvider
{
public string[] GetFiles(string path)
{
return Directory.GetFiles(path);
}
}

You would inject the abstraction in exactly the same way as you're injecting IFoo.
Now you can mock IFileProvider using Moq, creating a mock that returns exactly the strings that you want it to return.

var fileProvider = new Mock<IFileProvider>();
fileProvider.Setup(x => x.GetFiles(It.IsAny<string>()))
.Returns(new[] {"file1.txt", "file2.txt"});

You can also use Microsoft.Extensions.FileProviders.Physical which provides both the file system access and the abstraction.

public class Download
{
private readonly IFoo _foo;
private readonly Microsoft.Extensions.FileProviders.IFileProvider _fileProvider;

public Download(IFoo foo, IFileProvider fileProvider)
{
_foo = foo;
_fileProvider = fileProvider;
}

public void SomethingWithFiles()
{
var files = _fileProvider.GetDirectoryContents("filepath")
.Where(item => !item.IsDirectory);

foreach (var item in files)
{
// something
}
}
}

The concrete implementation would be PhysicalFileProvider.


One more variation. Instead of injecting an interface, inject a delegate:

public delegate string[] GetFilesFunction(string path);

public class Download
{
private readonly IFoo _foo;
private readonly GetFilesFunction _getFiles;

public Download(IFoo foo, GetFilesFunction getFiles)
{
_foo = foo;
_getFiles = getFiles;
}

...

That's even easier to mock. You don't even need Moq.

var subject = new Download(mockedFoo, path => new []{"file1.txt","file2.txt"} );

Mock File IO static class in c#

You can't mock static methods with Rhino mock. See this question for more info. You could create a facade class to wrap the file system calls you will use and then create a mock version of that.

Unit Testing File I/O

Check out Tutorial to TDD using Rhino Mocks and SystemWrapper.

SystemWrapper wraps many of System.IO classes including File, FileInfo, Directory, DirectoryInfo, ... . You can see the complete list.

In this tutorial I'm showing how to do testing with MbUnit but it's exactly the same for NUnit.

Your test is going to look something like this:

[Test]
public void When_try_to_create_directory_that_already_exists_return_false()
{
var directoryInfoStub = MockRepository.GenerateStub<IDirectoryInfoWrap>();
directoryInfoStub.Stub(x => x.Exists).Return(true);
Assert.AreEqual(false, new DirectoryInfoSample().TryToCreateDirectory(directoryInfoStub));

directoryInfoStub.AssertWasNotCalled(x => x.Create());
}

How do I write a unit test that relies on file system events?

If you are looking to test another object that uses this class my answer is not relevant.

When I write unit tests to operations I prefer using the ManualResetEvent

The unit test will be something like:

     ...
DirectoryProcessor.SourceFileChanged+=onChanged;
manualResetEvent.Reset();
File.WriteAllText();
var actual = manualResetEvent.WaitOne(MaxTimeout);
...

when manualResetEvent is the ManualResetEvent and the MaxTimeout is some TimeSpan (my advice always use the time out).
now we are missing the "onChanged":

     private void onChanged(object sender, SourceEventArgs e)
{
manualResetEvent.Set();
}

I hope this is helpful

Unit Testing a Class That Uses the File System

The simplest way make the file access mock-able while retaining control over the lifecycle of the disposable resources is to inject a StreamFactory into your class:

public class ReportBiz {

private IStreamFactory streamFactory;

public ReportBiz(IStreamFactory streamFactory) {
this.streamFactory = streamFactory
}

public void CreateReportFile() {
using(Stream stream = this.streamFactory.CreateStream()) {
// perform the important work!
}
}
}

When there's more business logic involved, your factory method may be a bit more elaborate, but not by much:

public void CreateReportFile() {
string sourcePath = this.otherComponent.GetReportPath();
using(Stream stream = this.streamFactory.CreateStream(sourcePath)) {
// perform the important work!
}
}

Mocking Registry and File System

OK, here's an example.

Given these classes:

public interface IRegistryActions
{
bool WriteValue(string key, string value);
}

public class RegistryActions : IRegistryActions
{
public bool WriteValue(string key, string value)
{
// pseudocode
// var key = Registry.OpenKey(key);
// Registry.WriteValue(key, value);
}
}

And this class that uses them: the class which will perform the actions is passed to the constructor in this example, but could as easily be a property. This means that whenever you want to actually use the class in your actual code, you can explicitly pass a class that implements IRegistryActions as a parameter - e.g. var exampleClass = new ExampleClass(new RegistryActions()); - or alternatively default to the actual implementation if passed null, i.e. this.registryActions = registryActions ?? new RegistryActions();

public class ExampleClass
{
private IRegistryActions registryActions;

public ExampleClass(IRegistryActions registryActions)
{
this.registryActions = registryActions;
}

public bool WriteValue(string key, string value)
{
return registryActions.WriteValue(key, value);
}
}

So in your unit test you want to verify that the call is made with the right parameters. How exactly you do this depends on what mocking framework you use, which is generally either a matter of personal choice or you use what's already used.

[Test]
public void Test_Registry_Writes_Correct_Values()
{
string key = "foo";
string value = "bar";

// you would normally do this next bit in the Setup method or test class constructor rather than in the test itself
Mock<IRegistryActions> mock = MockFramework.CreateMock<IRegistryActions>();

var classToTest = new ExampleClass(mock); // some frameworks make you pass mock.Object

// Tell the mock what you expect to happen to it
mock.Expect(m => m.WriteValue(key, value));

// Call the action:
classToTest.WriteValue(key, value);

// Check the mock to make sure it happened:
mock.VerifyAll();
}

In this you're asserting that your class has called the correct method on the interface, and passed the correct values.



Related Topics



Leave a reply



Submit