如何将解决方案资源作为FileStream对象传递给截获的File.Open命令?

时间:2010-08-12 09:37:19

标签: c# unit-testing file assemblies

我扩展了一个访问硬盘上文件的旧库。我的单元测试项目中有嵌入资源这样的文件。

我已经移植了部分库以接受流,这允许我使用GetManifestResourceStream将我的嵌入式资源传递给旧库。这很好,但这是一个轻微的麻烦。维护这些图书馆的人不喜欢“混乱”或不得不发布新版本。

JustMock和TypeMock允许我拦截File.Open命令,我希望将库传递给FileStream对象,但是如何从Embedded Manifest Resource构造FileStream对象? 我当然可以创建一个物理文件,但我不想在运行测试时触摸文件系统。

3 个答案:

答案 0 :(得分:2)

我根据您在此提到的要求烹制了样品。我在内存流中使用它,但也可以使用嵌入式资源。

           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]);
        }

由于我正在调用mscorlib成员而FileStream.Write是一个实例调用/不包含在默认设置File,DateTime,FileInfo中。我还在TestInitailization期间添加了以下行。

           Mock.Partial<FileStream>()
              .For<byte[], int, int>((x, content, offset, len) => 
            x.Write(content, offset, len));

[免责声明我为telerik工作]

希望有所帮助,

Mehfuz

答案 1 :(得分:1)

您可以阅读以下文章"Read embedded file from assembly""C#: Use Embedded Resource",其中演示了如何实现这一目标。

答案 2 :(得分:0)

您可以创建一个派生自FileStream的新类,可能称为FauxFileStream或其他东西,并覆盖其中所有必需的Stream相关方法,以转而使用其他流。然后,您可以轻松地实例化new FauxFileStream(myManifestResourceStream)并将其传递给库。

该课程看起来像这样:

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); }
}

但是,目前这不会编译,因为你需要使用一些合理的参数调用FileStream的基础构造函数。我能想到的最好的方法是传递打开一些文件进行读取的东西(这将是无害的,因为实际上不会读取文件,因为所有方法都被覆盖)。您可以为此创建一个0字节的文件,如果您将FileShare.Read作为共享参数传递,则可以安全地打开同一个文件以便同时读取。