C# 如何将解决方案资源作为FileStream对象传递(传递给截获的File.Open命令)?
我扩展了一个访问硬盘上文件的传统库。在我的单元测试项目中,我有诸如嵌入式资源之类的文件 我已经移植了库的一部分以接受流,这允许我使用GetManifestResourceStream将嵌入的资源传递给遗留库。这很好,但有点麻烦。维护这些库的人不喜欢“混乱”或不得不发布新版本 JustMock和TypeMock允许我截取File.Open commmand,我希望向库传递一个FileStream对象,但是如何从嵌入的清单资源构造FileStream对象呢?C# 如何将解决方案资源作为FileStream对象传递(传递给截获的File.Open命令)?,c#,unit-testing,file,assemblies,C#,Unit Testing,File,Assemblies,我扩展了一个访问硬盘上文件的传统库。在我的单元测试项目中,我有诸如嵌入式资源之类的文件 我已经移植了库的一部分以接受流,这允许我使用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:什么是“输入”,什么是“输出”,什么是“文件”,没有明确区分。我不确定是否必须针对文件写入操作进行断言。否则,我可以把它们删掉。当真正的文件系统也不抛出时,它们也不应该抛出。我不认为“脑死”是很有帮助的,但是与文件系统的松散耦合肯定是编程的一个方面,在它被制作的时候是不被欣赏的。谢谢你的意见。