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

C# 如何将解决方案资源作为FileStream对象传递(传递给截获的File.Open命令)?,c#,unit-testing,file,assemblies,C#,Unit Testing,File,Assemblies,我扩展了一个访问硬盘上文件的传统库。在我的单元测试项目中,我有诸如嵌入式资源之类的文件 我已经移植了库的一部分以接受流,这允许我使用GetManifestResourceStream将嵌入的资源传递给遗留库。这很好,但有点麻烦。维护这些库的人不喜欢“混乱”或不得不发布新版本 JustMock和TypeMock允许我截取File.Open commmand,我希望向库传递一个FileStream对象,但是如何从嵌入的清单资源构造FileStream对象呢? 当然,我可以创建一个物理文件,但我不希望

我扩展了一个访问硬盘上文件的传统库。在我的单元测试项目中,我有诸如嵌入式资源之类的文件

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

JustMock和TypeMock允许我截取File.Open commmand,我希望向库传递一个FileStream对象,但是如何从嵌入的清单资源构造FileStream对象呢?
当然,我可以创建一个物理文件,但我不希望在运行测试时触碰文件系统。

您可以阅读以下文章,其中演示了如何实现此目标。

我根据您在此处提到的要求制作了一个示例。我在内存流中使用它,但也可以使用嵌入式资源

           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]);
        }
byte[]actual=新字节[255];
//本地写入也可以从资源清单中完成。
使用(StreamWriter writer=newstreamwriter(newmemoryStream(实际)))
{
writer.WriteLine(“你好,世界”);
writer.Flush();
}
//安排文件系统。
FileStream fs=(FileStream)格式化程序服务
.GetSafeUninitializedObject(类型为(文件流));
//模拟特定的呼叫并设定期望。
Mock.Arrange(()=>fs.Write(Arg.IsAny(),Arg.AnyInt,Arg.AnyInt))
.DoInstead((字节[]内容,整数偏移量,整数长度)=>
{
实际复制到(内容、偏移量);
});
//返回File.Open的自定义文件流。
Mock.Arrange(()=>File.Open(Arg.AnyString,Arg.IsAny())
.报税表(fs);
//表演
var fileStream=File.Open(“hello.txt”,FileMode.Open);
byte[]fakeContent=新字节[actual.Length];
//原始任务
Write(fakeContent,0,实际的.Length);
//断言
Assert.Equal(fakeContent.Length、actual.Length);
对于(var i=0;i
因为我正在moking一个mscorlib成员和FileStream.Write是一个实例调用/不包含在默认的集合文件DateTime和FileInfo中。在测试化过程中,我还添加了以下行

           Mock.Partial<FileStream>()
              .For<byte[], int, int>((x, content, offset, len) => 
            x.Write(content, offset, len));
Mock.Partial()
.For((x,内容,偏移量,len)=>
x、 写入(内容、偏移量、长度);
[我为telerik工作的免责声明]

希望有帮助


Mehfuz

您可以创建一个从
FileStream
派生的新类,可能称为
FauxFileStream
或其他什么,并覆盖其中所有必要的
相关方法,以转到不同的流。然后,您可以轻松地实例化一个新的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
作为共享参数传递,您可以安全地同时打开相同的文件进行读取。

谢谢,但第一篇文章介绍了在文件系统上创建文件,这是我希望避免的(我的最后一段)。第二篇文章假设一个库接受一个流作为输入,这就是我在第2段中描述的。它也可以使用IgnoreArguments修饰符来代替Arg.IsAny。最后,我使用了FormatterServices,但也可以使用Mock.Create()创建FileStream实例,因为只使用了fs.Write,像FormatterServices这样的快捷方式可以大大提高测试性能。我可能需要两个底层流。我怀疑您是否能够写入清单资源流。但这当然是一种选择,而且这是一种值得复杂实用程序类使用的常见情况。由于底层类缺少正确类型的一个层次结构层,因此查看大量必需的管道代码,我开始觉得我应该研究这些动态语言。@Tormod:① 我怀疑你需要两条潜在的河流。您可以只实例化两个
FauxFileStream
s。② 您是否可以编写
ManifestResourceStream
,这无关紧要,我看不出这有什么关系。它只会扔,就像它应该扔的一样。③ 您遇到的问题不是静态键入的错误。你的库一定是头脑发热,需要截取
文件。使用mocking打开
。如果经过适当的考虑,它将只接受一个
,而您可以毫无困难地传入
ManifestResourceStream
。申诉1:不接受流。申诉2:什么是“输入”,什么是“输出”,什么是“文件”,没有明确区分。我不确定是否必须针对文件写入操作进行断言。否则,我可以把它们删掉。当真正的文件系统也不抛出时,它们也不应该抛出。我不认为“脑死”是很有帮助的,但是与文件系统的松散耦合肯定是编程的一个方面,在它被制作的时候是不被欣赏的。谢谢你的意见。