How can I pass a solution resource as a FileStream object (to an intercepted File.Open command)?
I extend upon a legacy library which accesses files on the harddrive. I have such files as embedded resources in my unit test project.
I have ported parts of the library to accept streams, which allows me to use GetManifestResourceStream to pass my embedded resource to the legacy library. This works fine, but it is a slight hassle. And people maintaining those libraries don't appreciate the "clutter" or having to publish new releases.
JustMock and TypeMock allows me to intercept the File.Open commmand, and I wish to pass the library a FileStream object, but how do I construct a FileStream Obje开发者_JAVA技巧ct from an Embedded Manifest Resource? I could of course create a physical file, but I don't wish to touch the file system while running tests.
I have cooked a sample based on the requirments you mentioned here. I used in memory stream for it but can done with embedded resource as well.
byte[] actual = new byte[255];
// writing locally, can be done from resource manifest as well.
using (StreamWriter writer = new StreamWriter(new MemoryStream(actual)))
{
writer.WriteLine("Hello world");
writer.Flush();
}
// arrange the file system.
FileStream fs = (FileStream)FormatterServices
.GetSafeUninitializedObject(typeof(FileStream));
// mocking the specific call and setting up expectations.
Mock.Arrange(() => fs.Write(Arg.IsAny<byte[]>(), Arg.AnyInt, Arg.AnyInt))
.DoInstead((byte[] content, int offset, int len) =>
{
actual.CopyTo(content, offset);
});
// return custom filestream for File.Open.
Mock.Arrange(() => File.Open(Arg.AnyString, Arg.IsAny<FileMode>()))
.Returns(fs);
// act
var fileStream = File.Open("hello.txt", FileMode.Open);
byte[] fakeContent = new byte[actual.Length];
// original task
fileStream.Write(fakeContent, 0, actual.Length);
// assert
Assert.Equal(fakeContent.Length, actual.Length);
for (var i = 0; i < fakeContent.Length; i++)
{
Assert.Equal(fakeContent[i], actual[i]);
}
Since i am moking a mscorlib member and FileStream.Write is a instance call / not contains in the default set File, DateTime, FileInfo. I also added the following line during TestInitailization.
Mock.Partial<FileStream>()
.For<byte[], int, int>((x, content, offset, len) =>
x.Write(content, offset, len));
[Disclaimer i work for telerik]
Hope that helps,
Mehfuz
You could read the following articles "Read embedded file from assembly" and "C#: Use Embedded Resource" which demonstrates how to achieve this goal.
You could create a new class deriving from FileStream
, perhaps called FauxFileStream
or something, and override all the necessary Stream
-related methods in it to go to a different stream instead. Then you can easily instantiate a new FauxFileStream(myManifestResourceStream)
and pass that to the library.
The class would look something like this:
class FauxFileStream : FileStream
{
private Stream _underlying;
public FauxFileStream(Stream underlying)
{
_underlying = underlying;
}
public override bool CanRead { get { return _underlying.CanRead; } }
public override bool CanSeek { get { return _underlying.CanSeek; } }
public override bool CanTimeout { get { return _underlying.CanTimeout; } }
public override bool CanWrite { get { return _underlying.CanWrite; } }
public override long Length { get { return _underlying.Length; } }
public override long Position { get { return _underlying.Position; } set { _underlying.Position = value; } }
public override int ReadTimeout { get { return _underlying.ReadTimeout; } set { _underlying.ReadTimeout = value; } }
public override int WriteTimeout { get { return _underlying.WriteTimeout; } set { _underlying.WriteTimeout = value; } }
public override void Close() { _underlying.Close(); }
public override void Flush() { _underlying.Flush(); }
public override int Read(byte[] buffer, int offset, int count) { return _underlying.Read(buffer, offset, count); }
public override int ReadByte() { return _underlying.ReadByte(); }
public override long Seek(long offset, SeekOrigin origin) { return _underlying.Seek(offset, origin); }
public override void SetLength(long value) { _underlying.SetLength(value); }
public override void Write(byte[] buffer, int offset, int count) { _underlying.Write(buffer, offset, count); }
public override void WriteByte(byte value) { _underlying.WriteByte(value); }
}
However, at the moment this doesn’t compile because you need to call the FileStream
’s base constructor with some reasonable arguments. The best I could think of is to pass something that opens some file for reading (which will be harmless because the file will not actually be read because all the methods are overridden). You could create a 0-byte file for this purpose and you can safely open the same file for reading simultaneously if you pass FileShare.Read
as the share parameter.
精彩评论