使用 Shims 对 ZipFile 进行单元测试
Unit Testing using Shims for ZipFile
我正在尝试对一些使用 ZipFile.OpenRead 的代码进行单元测试,以从 ZIP 中提取一些 XML 文件(使用最小起订量编写单元测试)
有没有办法用我自己的结果替换对 ZipFile.OpenRead 的调用?我曾在类似情况下使用垫片,但我不知道在这种情况下该怎么做,而且关于垫片的文档非常稀少。
这里是(部分)需要单元测试的方法:
public IEnumerable<ConfigurationViewModel> ExtractXmlFromZip(string fileName)
{
var configs = new List<ConfigurationViewModel>();
using (var archive = ZipFile.OpenRead(fileName))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (entry.FullName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
{
LoadConfigfromZipArchiveEntry(entry, configs)
}
}
}
return configs;
}
没有办法使用 mock 来模拟像 ZipFile 这样的静态 class,您可以使用 IZipFileWrapper 来包装它(比如)
public IZipFileWrapper
{
ZipArchive OpenRead(string fileName)
}
public ZipFileWrapper : IZipFileWrapper
{
public ZipArchive OpenRead(string fileName)
{
return ZipFile.OpenRead(fileName)
}
}
那么代码就变成了:
public MyObj
{
private IZipFileWrapper zipFileWrapper;
public MyObj(IZipFileWrapper zipFileWrapper)
{
this.zipFileWrapper = zipFileWrapper;
}
public IEnumerable<ConfigurationViewModel> ExtractXmlFromZip(string fileName)
{
var configs = new List<ConfigurationViewModel>();
// Call the wrapper
using (var archive = this.zipFileWrapper.OpenRead(fileName))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (entry.FullName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
{
LoadConfigfromZipArchiveEntry(entry, configs)
}
}
}
return configs;
}
}
并测试
[TestMethod]
public void ExtractXmlFromZip_Test()
{
var myThing = new MyObj();
var fileName = "my.zip";
ZipArchive myZipArchive = CreateTestZipFile(); // Set up your return
var mockWrapper = new Mock<IZipFileWrapper>();
mockWrapper.Setup(m => m.OpenRead(fileName)).Returns(myZipArchive);
var configs = myThing.ExtractXmlFromZip(fileName);
// Assert
}
}
您可能需要包装更多才能通过,但希望这能说明概念。
(在我意识到你问的是最小起订量而不是 Microsoft Fakes 的垫片之前写了这篇文章)
有一种更简单的方法可以使用可以获取此代码的 Shims - 来自 Microsoft Fakes。
ZipFile class 是 System.IO.Compression.FileSystem
的一部分,它在同名的 dll 中。
为了让我们使用 ShimZipFile
,我们需要添加一个 Fakes 程序集:
注意:我们需要伪造System.IO.Compression.FileSystem
(即dll)not System.IO.Compression
dlll(即ZipFile
的命名空间) .
其中应该对项目进行如下修改:
然后我们可以在测试中使用它,例如:
[TestMethod]
public void ExtractXmlFromZip_Test()
{
var myThing = new MyObj();
var fileName = "my.zip";
ZipArchive myZipArchive = CreateTestZipFile(); // Set up your return
using (ShimsContext.Create())
{
System.IO.Compression.Fakes.ShimZipFile.OpenReadString = (filename) => myZipArchive;
var configs = myThing.ExtractXmlFromZip(fileName);
// Assert
}
}
MSDN 上有关于 naming conventions 伪造填充程序的信息。
我最近采用的一种方法是在 class 上使用私有委托成员,其唯一目的是包装具体实现。
幸运的是,对我来说,R# 可以轻松创建具有单个私有成员的具体代理 class,公开包装类型上的 public 成员(通过委托成员代码生成器) ,然后抽取接口解耦,提供mock方式,测试时验证。
我正在尝试对一些使用 ZipFile.OpenRead 的代码进行单元测试,以从 ZIP 中提取一些 XML 文件(使用最小起订量编写单元测试)
有没有办法用我自己的结果替换对 ZipFile.OpenRead 的调用?我曾在类似情况下使用垫片,但我不知道在这种情况下该怎么做,而且关于垫片的文档非常稀少。
这里是(部分)需要单元测试的方法:
public IEnumerable<ConfigurationViewModel> ExtractXmlFromZip(string fileName)
{
var configs = new List<ConfigurationViewModel>();
using (var archive = ZipFile.OpenRead(fileName))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (entry.FullName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
{
LoadConfigfromZipArchiveEntry(entry, configs)
}
}
}
return configs;
}
没有办法使用 mock 来模拟像 ZipFile 这样的静态 class,您可以使用 IZipFileWrapper 来包装它(比如)
public IZipFileWrapper
{
ZipArchive OpenRead(string fileName)
}
public ZipFileWrapper : IZipFileWrapper
{
public ZipArchive OpenRead(string fileName)
{
return ZipFile.OpenRead(fileName)
}
}
那么代码就变成了:
public MyObj
{
private IZipFileWrapper zipFileWrapper;
public MyObj(IZipFileWrapper zipFileWrapper)
{
this.zipFileWrapper = zipFileWrapper;
}
public IEnumerable<ConfigurationViewModel> ExtractXmlFromZip(string fileName)
{
var configs = new List<ConfigurationViewModel>();
// Call the wrapper
using (var archive = this.zipFileWrapper.OpenRead(fileName))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (entry.FullName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
{
LoadConfigfromZipArchiveEntry(entry, configs)
}
}
}
return configs;
}
}
并测试
[TestMethod]
public void ExtractXmlFromZip_Test()
{
var myThing = new MyObj();
var fileName = "my.zip";
ZipArchive myZipArchive = CreateTestZipFile(); // Set up your return
var mockWrapper = new Mock<IZipFileWrapper>();
mockWrapper.Setup(m => m.OpenRead(fileName)).Returns(myZipArchive);
var configs = myThing.ExtractXmlFromZip(fileName);
// Assert
}
}
您可能需要包装更多才能通过,但希望这能说明概念。
(在我意识到你问的是最小起订量而不是 Microsoft Fakes 的垫片之前写了这篇文章)
有一种更简单的方法可以使用可以获取此代码的 Shims - 来自 Microsoft Fakes。 ZipFile class 是
System.IO.Compression.FileSystem
的一部分,它在同名的 dll 中。
为了让我们使用 ShimZipFile
,我们需要添加一个 Fakes 程序集:
注意:我们需要伪造System.IO.Compression.FileSystem
(即dll)not System.IO.Compression
dlll(即ZipFile
的命名空间) .
其中应该对项目进行如下修改:
然后我们可以在测试中使用它,例如:
[TestMethod]
public void ExtractXmlFromZip_Test()
{
var myThing = new MyObj();
var fileName = "my.zip";
ZipArchive myZipArchive = CreateTestZipFile(); // Set up your return
using (ShimsContext.Create())
{
System.IO.Compression.Fakes.ShimZipFile.OpenReadString = (filename) => myZipArchive;
var configs = myThing.ExtractXmlFromZip(fileName);
// Assert
}
}
MSDN 上有关于 naming conventions 伪造填充程序的信息。
我最近采用的一种方法是在 class 上使用私有委托成员,其唯一目的是包装具体实现。
幸运的是,对我来说,R# 可以轻松创建具有单个私有成员的具体代理 class,公开包装类型上的 public 成员(通过委托成员代码生成器) ,然后抽取接口解耦,提供mock方式,测试时验证。