I'm attempting to unit test some code that use ZipFile.OpenRead within to extract some XML files from a ZIP (Writing the unit tests with moq)
Is there a way I can replace the call to ZipFile.OpenRead with my own result? I have used shims for similar situations, but I can't figure out what to do in this situation and documentation on shims is pretty sparse.
Here is (part of) the method that needs unit-testing:
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;
}
There isn't a way to mock a static class like ZipFile using mock, you would either wrap it using an IZipFileWrapper (say)
public IZipFileWrapper
{
ZipArchive OpenRead(string fileName)
}
public ZipFileWrapper : IZipFileWrapper
{
public ZipArchive OpenRead(string fileName)
{
return ZipFile.OpenRead(fileName)
}
}
Then the code becomes:
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;
}
}
And a test of
[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
}
}
You would probably need to wrap more to get that passing, but hopefully that shows the concept.
System.IO.Compression.FileSystem
, which is in the dll of the same name.
To let us use a ShimZipFile
, we need to add a Fakes Assembly:
Note: We need to Fake System.IO.Compression.FileSystem
(i.e. the dll) not System.IO.Compression
dlll (which is the namespace of ZipFile
).
Which should make the following changes to the project:
Then we can use it in a test such as:
[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
}
}
There is info on MSDN about the naming conventions for shims that fakes generates.
One of the ways I have recently taken to is using a private delegated member on a class which has the single purpose of wrapping a concrete implementation.
Fortunately, for me, R# makes it easy to create a concrete proxy class with single private member, expose the public members on the wrapped type (through the Delegate Members code generator), then extract the interface to decouple, and provide ways to mock, and verify during testing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With