Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock FileStream with System.IO.Abstraction?

I'm trying to use the System.IO.Abstraction project along with System.IO.Abstraction.TestingHelpers to mock a FileStream.

This is the code that's using the file stream that I want to test:

private readonly IFileSystem _fileSystem;

public void ExtractImageAndSaveToDisk(IXLPicture xlPicture, string filePath)
{
    using (MemoryStream ms = new MemoryStream())
    {
        xlPicture.ImageStream.CopyTo(ms);

        using (FileStream fs = (FileStream)_fileSystem.FileStream.Create(filePath, FileMode.Create))
        {
            ms.CopyTo(fs);
            fs.Flush();
            fs.Close(); 
        }
    }
}

And this is how I've set up the testing:

[TestMethod]
public void CheckFileIsSavedToDisk()
{
    // Arrange
    var mockFileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
    {
        { @"c:\Test\Images\", new MockDirectoryData() },
    });

    var xlPicture = GetXLPicture();
    var filePath = @"c:\Test\Images\myimage.jpeg";

    var sut = new CostSheetImageExtractor(mockFileSystem);

    // Act
    sut.ExtractImagesAndSaveToDisk(xlPicture, filePath);
}

Running that I get the exception:

System.InvalidCastException: Unable to cast object of type 'System.IO.Abstractions.TestingHelpers.MockFileStream' to type 'System.IO.FileStream'.

on the using (FileStream fs = ... line.

The first thought was that I need to change the FileStream fs to use an interface that both the real and mock objects share, but as far as I can see there's no IFileStream interface that FileStream and MockFileStream share, so I'm guessing I'm doing this completely wrong? Is there actually a way to test this code with System.IO.Abstraction?

This existing answer seems to suggest this should be possible, I tried doing it that way too but got the same results.

like image 833
tomRedox Avatar asked Oct 22 '18 15:10

tomRedox


1 Answers

Not mocking the filesystem is by far the best option, as Chris F Carroll answer says, but if you want to really do that, your question almost contains the answer.

As you said, there is no such thing as an IFileStream interface, but if you look at the definition of FileStream you'll find that it inherits the abstract class Stream, and that's a common base class for all kinds of streamed data sources. The fake MockFileStream should also inherit from there.

So, try changing your code to use the abstract class instead:

using (Stream fs = (Stream)_fileSystem.FileStream.Create(filePath, FileMode.Create))
{
    ms.CopyTo(fs);
    fs.Flush();
    fs.Close(); 
}

It still retain all the base methods and your code just uses the base ones.

like image 185
Alejandro Avatar answered Nov 04 '22 09:11

Alejandro