Reputation: 2731
I have injected the System.IO.Abstractions.IFileSystem
interface into a class so that I can unit test file system interactions. There is one place in the class that uses new FileInfo(fileName)
. What is the replacement for that when using the IFileSystem
interface and MockFileSystem
?
Replacing File.OpenRead
with _fileSystem.File.OpenRead
is simple...
public string? Decrypt(string encryptedFilePath, string privateKeyArmor, string passPhrase)
{
try
{
using var privateKeyStream = new MemoryStream(Encoding.ASCII.GetBytes(privateKeyArmor));
using var encryptedFileStream = _fileSystem.File.OpenRead(encryptedFilePath);
var inputStream = PgpUtilities.GetDecoderStream(encryptedFileStream);
...
...but I don't know how to replace new FileInfo(fileName)
here.
private byte[] CompressFile(string fileName, CompressionAlgorithmTag algorithm)
{
var outputStream = new MemoryStream();
var compressedDataGen = new PgpCompressedDataGenerator(algorithm);
PgpUtilities.WriteFileToLiteralData(compressedDataGen.Open(outputStream), PgpLiteralData.Binary,
new FileInfo(fileName));
...
I tried _fileSystem.FileInfo.FromFileName(fileName)
, but that returns IFileInfo
instead of FileInfo
and the WriteFileToLiteralData
method won't take that.
Upvotes: 1
Views: 2107
Reputation: 3663
There is a helper function FileInfo.New(string fileName)
which can be used to create/use a mock IFileInfo
object
public class FileInfoTest
{
private readonly IFileSystem _fileSystem;
public FileInfoTest()
: this (new FileSystem())
{
}
internal FileInfoTest(IFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
public bool GetIsReadOnly(string path)
{
var info = _fileSystem.FileInfo.New(path);
return info.IsReadOnly;
}
}
To demonstrate this I have a physical file which is not read-only.
The first test, returns the IsReadonly state of the physical file.
The second, returns a mocked IFileInfo object with IsReadOnly set to true.
[TestMethod]
public void CheckFileInfoAgainstPhysicalFile()
{
var tester = new FileInfoTest();
var isReadOnly = tester.GetIsReadOnly(@"c:\dev\File.txt");
Assert.IsFalse(isReadOnly);
}
[TestMethod]
public void CheckFileInfoAgainstMock()
{
var mockFileInfo = new Mock<IFileInfo>();
mockFileInfo.SetupGet(mk => mk.IsReadOnly).Returns(true);
var mockFileSystem = new Mock<IFileSystem>();
mockFileSystem.Setup(mk => mk.FileInfo.New(@"c:\dev\File.txt")).Returns(mockFileInfo.Object);
var tester = new FileInfoTest(mockFileSystem.Object);
var isReadOnly = tester.GetIsReadOnly(@"c:\dev\File.txt");
Assert.IsTrue(isReadOnly);
}
As mentioned in the comment, the above doesn't address the basic problem - PgpUtilities doesn't know what an IFileInfo
is.
There is a way to fix this but it may not be worth the effort.
// Stand-in for External utility (returns a string so we can see it
// doing something with the original file)
public static class PgpUtilitiesOriginal
{
public static string WriteFileToLiteralData(Stream outputStream,
char fileType,
FileInfo file)
{
return file.Name;
}
}
// Interface for injection
public interface IPgpUtilties
{
string WriteFileToLiteralData(Stream outputStream,
char fileType,
IFileInfo file);
}
// Wrapper for the External Utility
public class DefaultPgpUtilities : IPgpUtilties
{
public string WriteFileToLiteralData(Stream outputStream, char fileType, IFileInfo file)
{
var instanceInfo = file.GetType().GetField("instance", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var instance = (FileInfo)instanceInfo.GetValue(file);
return PgpUtilitiesOriginal.WriteFileToLiteralData(outputStream, fileType, instance);
}
}
// Test Target
public class Tester
{
private readonly IFileSystem _fileSystem;
private readonly IPgpUtilties _pgpUtilities;
public Tester()
: this(new FileSystem(), new DefaultPgpUtilities())
{
}
public Tester(IFileSystem fileSystem, IPgpUtilties pgpUtilities)
{
_fileSystem = fileSystem;
_pgpUtilities = pgpUtilities;
}
public string Run(string fileName)
{
return _pgpUtilities.WriteFileToLiteralData(null, '\0', _fileSystem.FileInfo.FromFileName(fileName));
}
}
[TestMethod]
public void PhysicalFile()
{
var tester = new Tester();
var ret = tester.Run(@"c:\dev\file.txt");
Assert.AreEqual("file.txt", ret);
}
[TestMethod]
public void MockedFile()
{
var mockFileObject = new Mock<IFileInfo>();
var mockFileSystem = new Mock<IFileSystem>();
mockFileSystem.Setup(mk => mk.FileInfo.FromFileName(@"c:\dev\file.txt")).Returns(mockFileObject.Object);
var mockPgpUtilties = new Mock<IPgpUtilties>();
mockPgpUtilties.Setup(mk => mk.WriteFileToLiteralData(It.IsAny<Stream>(), It.IsAny<char>(), mockFileObject.Object)).Returns("Hello World");
var tester = new Tester(mockFileSystem.Object, mockPgpUtilties.Object);
var ret= tester.Run(@"c:\dev\file.txt");
Assert.AreEqual("Hello World", ret);
}
Again, sorry about the piss-poor reading of the original question on my part.
Upvotes: 1