Reputation: 631
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
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
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
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