views:

116

answers:

3

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 Object 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.

A: 

You could read the following articles "Read embedded file from assembly" and "C#: Use Embedded Resource" which demonstrates how to achieve this goal.

Damyan Bogoev
Thank you, but the first article describes creating the file on the file system, which is what I'm hoping to avoid (my last paragraph). The second article assumes a library that accepts a stream as an input, which is what I describe in the 2nd paragraph.
Tormod
+1  A: 

Hi Tormod, 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

Mehfuz
Instead of using Arg.IsAny, it can also be done using IgnoreArguments modifier. Finally, I used FormatterServices but FileStream instance can also be created using Mock.Create<T>(), as specifically only fs.Write is used, shortcuts like FormatterServices can greatly increase the test performance.
Mehfuz
A: 

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.

Timwi
I may need two underlying streams. I doubt that you would be able to write to a manifest resource stream. But this certainly is an option, and it is a common occurrence worthy of a sophisticated utility class. Looking at the amount of plumbing code that is necessary because underlying class is one hierarchical layer short of being the correct type, I start feeling that I should be looking into those dynamic languages.
Tormod
@Tormod: ① I doubt you need two underlying streams. You can just instantiate two `FauxFileStream` s. ② It is irrelevant whether you can write to a `ManifestResourceStream`, I don’t see how that matters. It’ll just throw, like it should. ③ The problem you’re running into is not the fault of static typing. Your library must be quite braindead to require the interception of `File.Open` using mocking. If it were properly thought-through, it would just accept a `Stream` and you could just pass in the `ManifestResourceStream` with no troubles at all.
Timwi
Grievance 1 : Not accepting a stream. Grievance 2 : No clear distinction on what is "input", what is "output" and what is "document". I'm not really sure wether or not I will have to assert against file write operations. Otherwise, I can just stub them out. They shouldn't throw when the real file system doesn't throw, either.I don't think "braindead" is very helpful, but loose coupling to the file system is certainly an aspect of programming that wasn't appreciated at time it was made.Thank you for your input.
Tormod