In most of my project I create a static class containing all the functions I use all in my project. But when it comes to unit testing, I feel like there's a big flaw in my design, since I can't use Moq to Mock this static class.
I can provide a simple example with one project I'm working on that copies a lot of files, so I made a little helper to avoid redundancy in my code.
public static class Helpers
{
public static void CopyFile(string sourcePath, string destPath)
{
Log.Verbose("Start copy from {SourcePath} to {DestPath}", sourcePath, destPath);
if (!Directory.Exists(Path.GetDirectoryName(destPath)))
{
Directory.CreateDirectory(Path.GetDirectoryName(destPath));
}
File.Copy(sourcePath, destPath, true);
Log.Verbose("End of copy from {SourcePath} to {DestPath}", sourcePath, destPath);
}
}
So basically any code that calls this function is untestable, since I can't mock a static function inside a static class which also relies on System.IO
I tried to use System.IO.Abstraction and dependency injection to solve this issue, and I then have the following code:
public interface IHelpers
{
public void CopyFile(string sourcePath, string destPath);
}
public class Helpers(IFileSystem fileSystem) : IHelpers
{
private readonly IFileSystem FileSystem = fileSystem;
public void CopyFile(string sourcePath, string destPath)
{
Log.Verbose("Start copy from {SourcePath} to {DestPath}", sourcePath, destPath);
if (!FileSystem.Directory.Exists(Path.GetDirectoryName(destPath)))
{
FileSystem.Directory.CreateDirectory(Path.GetDirectoryName(destPath));
}
FileSystem.File.Copy(sourcePath, destPath, true);
Log.Verbose("End of copy from {SourcePath} to {DestPath}", sourcePath, destPath);
}
}
Now I just need to inject my IHelper wherever I need to call CopyFile and everything seems fine. Except I have some rare occasions where it just feels wrong to inject this helper inside a class.
public class Component
{
public string Name;
public string Path;
public void ExportOutput()
{
// ...
// much calculation then...
// multiple calls to Helpers.CopyFile(src,dest);
// ...
}
}
To solve this, I passed the IHelper class as a parameter to the ExportOutput, but this seems absolutely disgusting.
The only solution I found to this issue was to remove the ExportOutput function and to put it somewhere where I could rely on dependency injection to use the Helper class.
Is this right or am I completely missing the point?
How do you generally handle those small helper function in you code?
ExportOutput
-function? But anyway , why do you find this "disgusting"? It's more or less what I'd expected as well.new()
anything up, in particular when these things introduce dependencies outside your control.