royalTS
royalTS

Reputation: 631

Unit testing of class using File.OpenRead()

I would like to test a class that is using the method File.OpenRead() to obtain the content of a file. After reading the content of the file then it processes them. I have create an interface and a class that wraps the static OpenRead() method. But I encounter the problem that OpenRead() returns a FileStream and I have no idea how to "mock" the file stream.

Currently, I am creating a file just to create a FileStream... Of course, the tests are regularly failing with a IOException because the file is still in use...

Stripped Example: Class:

class FileProcessor
{
  public FileProcessor(IFileWrap fileWrap) // fileWrap only redirects the calls to the static methods of File class
  { ... }

  public void Process(string file)
  {
    var content = fileWrap.ReadAllLines(file);

    // process content
  }
}

And the test:

[TestClass]
public class FileProcessor_Test
{
  [TestMethod]
  Process_FileNotReadable_Exception()
  {
    File.WriteAllText(testFile, "something");
    var fileWrapMock = new Mock<IFileWrap>();
    FileProcessor dut = new FileProcessor(fileWrapMock.Object);
    var actualException = AssertException.Throws<Exception>(() => dut.Process(testFile));
  }
}

I would like to avoid creating an abstraction of FileStream, too.

I was hoping that I could create a MemoryStream and somehow use this as the input, but this would require to change the file wrapper and deviate from the actual File class.

Any input is appreciated :)

Edit: The processing includes computing a MD5 checksum by calling ComputeHash() from the the class MD5.

Upvotes: 3

Views: 2980

Answers (3)

nvoigt
nvoigt

Reputation: 77285

Your original method is tightly coupled to the fact that it operates on a file. Don't do that. Make the method take a Stream, any stream. You can operate on a FileStream, or you can pass a MemoryStream for testing.

public void Process(string file)

Should be

public void Process(Stream stream)

If you want to, you could have a second method overload for convenience:

public void Process(string file)
{
    using (var stream = new FileStream(file, FileMode.Open))
    {
        this.Process(stream);
    }
}

That indeed cannot and should not be unit tested... it's .NET code handling external resources, at some point you have to trust the framework.


To address your edit: most Framework classes do something similar, for example the MD5 class has a ComputeHash method that works on a stream.

Upvotes: 4

Leonid
Leonid

Reputation: 1091

I would suggest you to make IFileWrap return a string (read all lines) instead of MemoryStream. Basically, It is possible to create an abstraction over File, that will encapsulate File inside it.

Upvotes: 1

Vijayanath Viswanathan
Vijayanath Viswanathan

Reputation: 8541

First of all, you don't need to unit test 'File' class as 'OpenRead' is a static method of 'File' class which you cannot decouple. It is not ideal to write a unit test against anything which has a dependency. In your case, you can mock 'IFileWrap' and create a mock method for 'ReadAllLines'. When unit test hits 'fileWrap.ReadAllLines(file)' method it will call the mock method instead of going to 'File.OpenRead()' method.

Upvotes: 2

Related Questions