You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
For one of my project I started to write a basic wrapper around the methods of System.IO.Compression.ZipFile. To achieve that I'm of course using System.IO.Abstractions.
I started to introduce 4 new types IZipFileFactory, ZipFileFactory, MockZipFileFactory, MockZipFileData. Now I would like to have a property ZipFile of type IZipFileFactory in IFileSystem so I can write something like
We did something like this at the company I work for (will hopefully open source some of this stuff soon).
It's actually quite nice to be able to take a ZipArchive on one IFileSystem and extract it to another (e.g. take a real ZIP on disk and populate a MockFileSystem with it), so we implemented this as extension methods on FileBase and ZipArchive:
etc.
Note that ZipArchive already works against the Stream APIs instead of files directly, so this is all pretty easy.
The advantage of that is that it works against any IFileSystem implementation (and across them), and that you don't need to implement this stuff in the IFileSystem implementations themselves.
Reacted by Tatham Oddie, Florian Greinacher and Daniel Park
@fgreinacher Well, at the time I started to write this it looked like the more natural way to do it for me. I have to give @herebebeasties' solution a try.
Might want to consider not locking on Zip files only, and consider a more generic ICompressorStream and IVirtualFileSystem, a virtual filesystem supplied by a zip file, or vhd or similar.
I've had thoughts of doing VHD manipulation somehow using System.IO.Abstractions since VHD and ZIP files basically represent virtual filesystems with varying degrees of functionality.
Your extension methods might make an excellent candidate for a SysIOAbstractions Contrib project or SysIOVirtualFileSystems
The only reason I think it'll be easier to be in a separate library is due to the widely varying platform support of various codecs and compression algos at that point.
@herebebeasties Any updates on open-sourcing that implementation? I am looking to make my usages of ZipArchive testable but am not sure how to start on the implementation you hinted at here. Would really appreciate if you could point me in the right direction to get me going.
I can see that FileBase has access to the underlying IFileSystem which can help you write these extension methods, but what about ExtractToDirectory(this ZipArchive zipArchive, string path); on ZipArchive? How do you get the IFileSystem in there?
Or that, with a destination IFileSystem also passed in. Sorry, that was missing in my original, have updated. Anyway, lots of ways to skin this particular cat!
Forgive me but I am trying to understand the reason for not implementing coverage of ZipFile in System.IO.Abstractions. If we are to use an extension method, doesn't that mean everyone who needs an interface on ZipFile will need to write an extension, where as if it was in the project in could be reused?
(If you use an extension method, how do you mock it?)
In the corresponding unit tests ZipFileTests.cs you can see, how such tests could look...
Even if the interfaces are not 100% compatible, you could either give my project a try or use the extension methods as a basis for implementing something similar for System.IO.Abstractions...
The easiest way to get around this issue is not to use ZipFile at all. It's a wrapper which wasn't designed to be unit tested at all. Instead, use what it wraps internally:
var_fileSystem=newFileSystem();// Usually injected in your classusingvararchiveStream=_fileSystem.FileStream.New("/path/to/file.zip",FileMode.CreateNew,FileAccess.Write,FileShare.None,bufferSize:0x1000,useAsync:false);usingvararchive=newZipArchive(archiveStream,ZipArchiveMode.Create,leaveOpen:false);vararchiveEntry=archive.CreateEntry("file.txt",CompressionLevel.SmallestSize);usingvararchiveEntryStream=archiveEntry.Open();usingvararchiveFileStream=_fileSystem.FileStream.New("/path/to/file.txt",FileMode.Open);archiveFileStream.CopyTo(archiveEntryStream);
Then just use MockFileSystem to make your assertions.
public static void ExtractToDirectory(this ZipArchive zipArchive, IFileSystem destFs, string destPath);
I thought I'd comment this sample implementation in case it is helpful:
public static void ExtractToDirectory(this ZipArchive zipArchive, IFileSystem destFs, string destPath)
{
ArgumentNullException.ThrowIfNull(zipArchive);
ArgumentNullException.ThrowIfNull(destFs);
if (string.IsNullOrEmpty(destPath)) throw new ArgumentNullException(nameof(destPath));
foreach (var entry in zipArchive.Entries)
{
var entryPath = destFs.Path.Combine(destPath, entry.FullName);
if (string.IsNullOrEmpty(entry.Name))
{
destFs.Directory.CreateDirectory(entryPath);
continue;
}
var directoryPath = destFs.Path.GetDirectoryName(entryPath);
destFs.Directory.CreateDirectory(directoryPath);
using var entryStream = entry.Open();
using var outputStream = destFs.File.Create(entryPath);
entryStream.CopyTo(outputStream);
}
}
Activity
herebebeasties commentedon Nov 5, 2018
We did something like this at the company I work for (will hopefully open source some of this stuff soon).
It's actually quite nice to be able to take a
ZipArchiveon oneIFileSystemand extract it to another (e.g. take a real ZIP on disk and populate aMockFileSystemwith it), so we implemented this as extension methods onFileBaseandZipArchive:etc.
Note that
ZipArchivealready works against theStreamAPIs instead of files directly, so this is all pretty easy.The advantage of that is that it works against any
IFileSystemimplementation (and across them), and that you don't need to implement this stuff in theIFileSystemimplementations themselves.fgreinacher commentedon Jan 5, 2019
I like the simple, extension-method based approach.
@samds Is there any major advantage in your proposal?
samds commentedon Jan 12, 2019
@fgreinacher Well, at the time I started to write this it looked like the more natural way to do it for me. I have to give @herebebeasties' solution a try.
ericnewton76 commentedon Jan 16, 2019
Might want to consider not locking on Zip files only, and consider a more generic ICompressorStream and IVirtualFileSystem, a virtual filesystem supplied by a zip file, or vhd or similar.
I've had thoughts of doing VHD manipulation somehow using System.IO.Abstractions since VHD and ZIP files basically represent virtual filesystems with varying degrees of functionality.
Your extension methods might make an excellent candidate for a SysIOAbstractions Contrib project or SysIOVirtualFileSystems
The only reason I think it'll be easier to be in a separate library is due to the widely varying platform support of various codecs and compression algos at that point.
fgreinacher commentedon Apr 19, 2019
Closing this. If you want to get a separate project in the SIOA org feel free to ping me here!
EricEzaM commentedon Sep 13, 2022
@herebebeasties Any updates on open-sourcing that implementation? I am looking to make my usages of
ZipArchivetestable but am not sure how to start on the implementation you hinted at here. Would really appreciate if you could point me in the right direction to get me going.I can see that
FileBasehas access to the underlyingIFileSystemwhich can help you write these extension methods, but what aboutExtractToDirectory(this ZipArchive zipArchive, string path);onZipArchive? How do you get theIFileSystemin there?herebebeasties commentedon Sep 13, 2022
I've since left the company where I did this, but I can perhaps find some time to get you going if you can wait until the weekend.
EricEzaM commentedon Sep 13, 2022
That would be good, thanks. Most basic question from would be how do you get
IFileSystemin theZipArchiveextension method?herebebeasties commentedon Sep 13, 2022
Well it's an extension method so just something like:
EricEzaM commentedon Sep 13, 2022
Ah, right, thanks. I suppose I was taking your sample code below a little to literally then, with it being an ext method on ZipArchive?
herebebeasties commentedon Sep 13, 2022
Or that, with a destination
IFileSystemalso passed in. Sorry, that was missing in my original, have updated. Anyway, lots of ways to skin this particular cat!greg0rym commentedon Oct 21, 2022
Forgive me but I am trying to understand the reason for not implementing coverage of ZipFile in System.IO.Abstractions. If we are to use an extension method, doesn't that mean everyone who needs an interface on ZipFile will need to write an extension, where as if it was in the project in could be reused?
(If you use an extension method, how do you mock it?)
vbreuss commentedon Oct 27, 2022
@greg0rym :
I created a library that abstracts the
ZipFilemethods, so that they can be abstracted via the underlyingIFileSysteminterface:In the corresponding unit tests ZipFileTests.cs you can see, how such tests could look...
Even if the interfaces are not 100% compatible, you could either give my project a try or use the extension methods as a basis for implementing something similar for
System.IO.Abstractions...sakopov commentedon Feb 3, 2023
The easiest way to get around this issue is not to use
ZipFileat all. It's a wrapper which wasn't designed to be unit tested at all. Instead, use what it wraps internally:Then just use
MockFileSystemto make your assertions.cdMOE commentedon Jan 24, 2025
I thought I'd comment this sample implementation in case it is helpful: