0

I'm trying to write a unit test that writes to a file it opens with File.OpenWrite().

I'm wrapping File with SystemWrapper's IFileWrap interface. In production, I'm using SimpleInjector to inject an instance of SystemWrapper's FileWrap class, and that's working fine. But I'm trying to mock IFileWrap in my unit tests, using MOQ, and that's not working.

I'm new to SystemWrapper, and I'm doing my best to figure out how it is intended to be used. As far as I can tell, IFileWrap.OpenWrite() returns an IFileWrap instance, from which you can obtain the stream with FileStreamInstance.

So, in my class under test, I inject an IFileWrap in my constructor:

public class ClassUnderTest
{
    private readonly IFileWrap fileWrap;

    public ClassUnderTest(IFileWrap fileWrap)
    {
        this.fileWrap = fileWrap;
    }

    ...
}

And in my method under test, I get the stream from FileStreamInstance:

var fsWrap = this.fileWrap.OpenWrite(fullPath);
var ostream = fswrap.FileStreamInstance;

That works fine, in production, where fileWrap is instantiated with an instance of FileWrap. But in my tests, I'm trying to create a Mock for File.OpenWrite that returns a mocked FileStream:

var fileStreamMock = new Mock<IFileStreamWrap>();

var fileMock = new Mock<IFileWrap>();
fileMock.Setup(fm => fm.OpenWrite(It.IsAny<string>())).Returns(fileStreamMock.Object);

var classUnderTest = new ClassUnderTest(fileMock.Object);

And when I walk through the method under test, in the debugger, from my unit test, fsWrap.FileStreamInstance is null, when I'd expect it to be my mocked filestream.

Any ideas as to what I am doing wrong?

Jeff Dege
  • 11,190
  • 22
  • 96
  • 165

2 Answers2

1

You're not stubbing FileStreamInstance to return anything, so Moq will return null.

You need to set up to return something for FileStreamInstance after creating your IFileStreamWrap mock:

var fileStreamWrapMock = new Mock<IFileStreamWrap>(); // Was fileStreamMock
var fileStreamMock = new Mock<FileStream>();
fileStreamWrapMock.Setup(fswm => fswm.FileStreamInstance).Returns(fileStreamMock);
Chris Mantle
  • 6,595
  • 3
  • 34
  • 48
  • When I try to mock FileStream I get an error - it doesn't have a zero-parameter constructor. But you pointed me in a useful direction. I set FileStreamInstance to return a FileStream to a temp file, that I'm deleting in TestCleanup. (I'm afraid I have to use a FileStream, here, and FileStream doesn't support NUL: or other non-file streams.) – Jeff Dege Mar 19 '15 at 19:44
1

I've been struggling with this just recently. I resolved it initially by using temp files, as you suggested in comments on Chris Mantle's answer. I recently stumbled on another way, using StreamInstance instead of FileStreamInstance (not sure if this was available when you posed the question):

var outBuffer = new byte[512];
var fileWrap = new Mock<IFileWrap>();
var fileStreamWrap = new Mock<IFileStreamWrap>();
var stream = new Mock<IFileStreamWrap>();
fileStreamWrap.Setup(fsw => fsw.StreamInstance).Returns(new MemoryStream(outBuffer));

//I'm using File.Create to create temporary files with DeleteOnClose flag in the application, which get disposed before the function returns
fileWrap.Setup(fw => fw.Create(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<FileOptions>()))
    .Returns((string filepath, int i, FileOptions fo) =>
    {
        //I'm using filepath in a collection in my test to handle multiple files created in the application code
        return fileStreamWrap.Object;
    });


//Call application code that creates temporary file and closes eagerly with dispose.
//Application code will be something like:
var tempFileStream = _fileWrap.Create(tempFileName, 4096, FileOptions.DeleteOnClose);
using (var writer = new StreamWriter(tempFileStream.StreamInstance, Encoding.Default, 4096, true))
{...}


//Test file contents were written correctly into outBuffer
var outString = System.Text.Encoding.UTF8.GetString(outBuffer);
Assert.AreEqual("Important Business string", outString);
Rob Hinchliff
  • 452
  • 1
  • 8
  • 14