19

I have been trying to mock out a using with a file stream but have not been able to complete this and am unsure how to do it, I am using rhino mock.

private Connection LoadConnectionDetailsFromDisk(string bodyFile)
{     
   //logic before
   using (FileStream fs = File.Open(bodyFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
   {
     return this.serverConfiguration.LoadConfiguration(fs, flowProcess);
   }
    //more logic
}

Could anyone tell me how to mock the using(FileStream....) so I am able to access this branch of the code?

GEOCHET
  • 21,119
  • 15
  • 74
  • 98
user101010101
  • 1,609
  • 6
  • 31
  • 52

6 Answers6

15

You have to abstract File.Open() by an interface method then you would be able mocking call to it.

So

1) Create an interface:

public interface IFileDataSource
{
   FileStream Open(string path,
                   FileMode mode,
                   FileAccess access,
                   FileShare share);
}

2) Change LoadConnectionDetailsFromDisk() as following:

private Connection LoadConnectionDetailsFromDisk(string path, IFileDataSource fileSource)
{     
   using (FileStream fs = fileSource.Open(bodyFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
   {
      return this.serverConfiguration.LoadConfiguration(fs, flowProcess);
   }

   //more logic
}

3) In a test mock the interface and inject a mock

// create a mock instance
var sourceMock = MockRepository.GenerateMock<IFileDataSource>();

// setup expectation
sourceMock.Expect(m => m.Open("path", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
         .CallBack(
 delegate (string path, FileMode mode, FileAccess access, FileShare share)
 {
      // handle a call

     return true;
 }).Repeat.Any();

// TODO: depends on how you are triggering LoadConnectionDetailsFromDisk method call
// inject a mock

Considering that LoadConnectionDetailsFromDisk() you can not inject mock directly to this method call froma test so please show how this method is invoked.

sll
  • 61,540
  • 22
  • 104
  • 156
  • How should I call `LoadConnectionDetailsFromDisk` in normal code (outside of tests) ? – jing Jul 25 '18 at 10:35
4

The System.IO.Abstractions project and NuGet package also allow for mocking of FileStreams.

To use that you have to slightly change how you get the FileStream in the first place, to something like:

private readonly IFileSystem _fileSystem; // this is from the third-party System.IO.Abstractions package

// This is assuming dependency injection to insert the mock file system during testing, 
// or the real one in production
public YourConstructor(IFileSystem fileSystem)
{
   _fileSystem = fileSystem;
}

private Connection LoadConnectionDetailsFromDisk(string bodyFile)
{     
   using (Stream fs = _fileSystem.FileStream.Create(bodyFile, FileMode.Open))
   {
       return this.serverConfiguration.LoadConfiguration(fs, flowProcess);
   }

   //more logic
}
Dai
  • 141,631
  • 28
  • 261
  • 374
tomRedox
  • 28,092
  • 24
  • 117
  • 154
4

Maybe you can use the below code snippet for mocking FileStream

Mock<FileStream> mockFileStream = new Mock<FileStream>(new IntPtr(1), FileAccess.Read);

In this way, you create a new instance of FileStream, and you can use it like below

mockObject.Setup(s => s.GetFileStream(It.IsAny<string>())).Returns(mockFileStream.Object);

mockObject is any og your Interface to return FileStream

N Krishnia
  • 305
  • 2
  • 8
  • 1
    This used to be a nice solution, but unfortunately fails with .NET 6.0, as some additional verifications for valid handles have been added. – PMF Feb 28 '22 at 08:35
1

You can use SystemWrapper lib for this purpose. It contains interfaces and classes wrapping system classes and enables you to unit tests methods using this classes.

dragan.stepanovic
  • 2,955
  • 8
  • 37
  • 66
0

I would refactor the method so that the FileStream is passed to the method so that you can create a mock of the filestream

private Connection LoadConnectionDetailsFromDisk(FileStream bodyFile)
{ 
  ....
}

if you really want to get fancy you can always accept a IStream and then Mock that IStream but Rhino support creating mocks of concreate classes

 MockRepository mocks = new MockRepository();
 FileStream basket = mocks.CreateMock<FileStream>();
Micah Armantrout
  • 6,781
  • 4
  • 40
  • 66
0

I handled it by creating a FileStream subclass. The base constructor does require that we open an actual file, so I include a simple text file set to "Content" and "Include if newer", which we open read-only and sharable.

Then I overwrite the method that I actually need. In my case, I need the write method, and I need to do something with what is written, so I inject a callback to invoke.

private class CustomFileStream : FileStream
{
    private Action<string> WriteAction { get; }
    public CustomFileStream(Action<string> writeAction)
        // FileStream is notoriously hard to mock, so we inherit from it and we are a lightweight, real (but unused) FileStream underneath
        : base(Path.Combine(new ExecutionDirectory().Path /* Handle the execution directory in your own way */, "Content/DummyTextFile.txt"), FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
    {
        this.WriteAction = writeAction;
    }

    public override bool CanWrite => true;
    public override void Write(ReadOnlySpan<byte> buffer)
    {
        this.WriteAction(Encoding.UTF8.GetString(buffer));
    }
}
Timo
  • 7,992
  • 4
  • 49
  • 67