I am using Autofac as the DI container in a project. Now i have started to create unit tests using Moq in it. As the code for business class is already written, so i would like to avoid making major changes in the Business classes. I am facing problem in mocking System.IO.XXX classes (like FileSystemWatcher, Directory, File, StreamReader, etc). Mostly because either they are static classes or they do not have any interfaces.
// This is what one of my business class looks like
internal class SpanFileReader : ISpanFileReader
{
// Some private variables
private string _filePath;
private readonly ISpanLogger _spanLogger;
#region Public Properties
// Prorperties....
#endregion
#region Constructor
public SpanFileReader(string filePath)
{
_filePath = filePath;
_spanLogger = IocContainer.Instance.Container.Resolve<ISpanLogger>();
}
#endregion
#region Public Methods
public bool ReadSpanRecords(CancellationToken ct)
{
try
{
if (!VerifySpanFile())
return false;
_spanFileLines = new List<string>();
using (var streamReader = new StreamReader(_filePath))
{
while (!streamReader.EndOfStream)
{
// some logic
}
return true;
}
}
catch (OperationCanceledException operationCanceledException)
{
_spanLogger.UpdateLog("some message");
throw;
}
catch (Exception ex)
{
_spanLogger.UpdateLog("some message);
throw;
}
}
public void MoveFileToErrorFolder(string spanFileName)
{
var spanFilePath = AppConfiguration.SpanFolderPath + spanFileName;
var errorFilePath = AppConfiguration.SpanErrorFolderPath + spanFileName;
try
{
if (File.Exists(spanFilePath))
{
if (!File.Exists(errorFilePath))
{
_spanLogger.UpdateLog("some message");
File.Move(spanFilePath, errorFilePath);
_spanLogger.UpdateLog("some message");
}
else
{
File.Delete(spanFilePath);
_spanLogger.UpdateLog("some message");
}
}
else
{
_spanLogger.UpdateLog("some message");
}
}
catch (Exception ex)
{
_spanLogger.UpdateLog("some message");
throw ex;
}
}
}
Now i would like to use the StreamReader() in such a way that i can resolve its instance via some interface(say IStreamReader) using Autofac. So that while writing unit test for SpanFileReader(), i can register IStreamReader’s Moq instace in the container and use it instead of the actual instance.
I would like to do something similar with the File() class as well, so that i can provide my own moq implementation, which gets called when the SUT (the SpanFileReader instace) is tested.
Can someone please suggest a proper way to go about these scenarios.
You mention two problems:
How to mock
StreamReader?IStreamReaderinterface by copying the methods fromStreamReader.StreamReaderWrapperwhich implementsIStreamReader.StreamReader.e.g.
Now replace
new StreamReader()in your code, using Autofac (or any factory/IoC container). When testing, return aMock<IStreamReader>()instead:How to mock
File?Do the same as above, instead creating a
IFileandFileWrapperclass with no constructor / wrapped object.Instantiate an instance of this wrapper for use by the whole class, and use this instance whereever you would normally use
File.E.g.
then (from your example):
I find this a very clean pattern once used to it, as all constructor/method signatures are preserved. Having tests which do not touch the file system and are easily mocked is a huge advantage, and much faster too.
An important tip is to avoid adding any logic at all to the wrappers, otherwise they may need testing too (so you’d have to create a wrapper wrapper …)!
Classes like
Filecan also be accessed via an instance, so you might like to create separate interfaces to keep the instance and static methods wrapper separately (IFileStatics?).One further idea is to build up a library of various wrapped .Net classes for future use, or create the wrappers automatically with various .NET technologies (Castle Dynamic Proxy for example).