How do I MOQ the System.IO.FileInfo class... or any other class without an interface?

Viewed 14261

I am writing a number of unit tests for a logger class I created and I want to simulate the file class. I can't find the interface that I need to use to create the MOQ... so how do you successfully MOQ a class without an interface?

It also isn't clear to me how I can use dependency injection without having an interface available:

private FileInfo _logFile;

public LogEventProcessorTextFile(FileInfo logFile) {
    _logFile = logFile;
}

When I really want to do something like this (note IFileInfo instead of FileInfo):

private IFileInfo _logFile;

public LogEventProcessorTextFile(IFileInfo logFile) {
    _logFile = logFile;
}
5 Answers

Add the following nuget package to your csproj:

<ItemGroup>
  <PackageReference Include="System.IO.Abstractions" Version="13.2.29" />
</ItemGroup>

Inject System.IO.Abstractions.FileSystem into your class:

  public class FileLogger
  {
    private readonly IFileSystem fileSystem;

    public FileLogger(System.IO.Abstractions.IFileSystem fileSystem) // new FileSystem()
    {
      this.fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
    }
    
    public void LogEventProcessorTextFile(string filePath)
    {
      IFileInfo fileInfo = fileSystem.FileInfo.FromFileName(filePath);

      LogEventProcessorTextFile(fileInfo);
    }

    private System.IO.Abstractions.IFileInfo _logFile;

    public void LogEventProcessorTextFile(IFileInfo logFile) {
      _logFile = logFile;
    }
  }

Moq examples:

private Mock<IFileSystem> FileSystemMock { get; set; }

[SetUp]
public void Setup()
{
  var file = Mock.Of<IFileInfo>();
  var directoryInfo = Mock.Of<IDirectoryInfo>();

  Mock.Get(file).Setup(c => c.Exists).Returns(true);

  FileSystemMock = new Mock<IFileSystem>();

  FileSystemMock.Setup(c => c.FileInfo.FromFileName(It.IsAny<string>())).Returns(file);
  FileSystemMock.Setup(c => c.DirectoryInfo.FromDirectoryName("Settings")).Returns(directoryInfo);
}

This is more limited in that you can only use exact files, but can also be good because you control exactly what is in your file for the unit test....You could create a folder in your unit test project to keep pretend log files in. Then get current directory in your unit test using:

var currentDirectory = Environment.CurrentDirectory

then hop one level down into your test folder and grab the test files with:

var testFileInfos = Directory.GetFiles(
    Path.Combine(currentDirectory, "testFolderName"));

Then use those FileInfo objects as needed in your tests.

Related