Skip to main content

Testably.Abstractions.Compression

Wraps System.IO.Compression.ZipFile and System.IO.Compression.ZipArchive as extensions on IFileSystem.

Installation​

dotnet add package Testably.Abstractions.Compression

Latest version: 7.2.0

The extensions live in the Testably.Abstractions namespace.

Why this package exists​

System.IO.Compression.ZipFile operates on real string paths and System.IO.Compression.ZipArchive operates on Streams; neither knows about IFileSystem. The package bridges the two:

// With Testably.Abstractions.Compression, one line:
fileSystem.ZipFile.CreateFromDirectory("artworks", "artworks.zip");
Without the companion package (~15 lines of glue)
using MemoryStream buffer = new();
using (ZipArchive archive = new(buffer, ZipArchiveMode.Create, leaveOpen: true))
{
AddDirectory("", "artworks");

void AddDirectory(string prefix, string directory)
{
foreach (string file in fileSystem.Directory.GetFiles(directory))
{
ZipArchiveEntry entry = archive.CreateEntry(prefix + Path.GetFileName(file));
using Stream entryStream = entry.Open();
entryStream.Write(fileSystem.File.ReadAllBytes(file));
}
foreach (string sub in fileSystem.Directory.GetDirectories(directory))
{
string name = fileSystem.Path.GetFileName(sub);
archive.CreateEntry(prefix + name + "/");
AddDirectory(prefix + name + "/", sub);
}
}
}
buffer.Position = 0;
using FileSystemStream output = fileSystem.File.Create("artworks.zip");
buffer.CopyTo(output);

Extraction looks similar: open a ZipArchive in Read mode, loop the entries, recreate directories on the file system, copy each entry's stream into fileSystem.File.WriteAllBytes.

The package replaces this glue with a one-line call. Beyond brevity, it also gives you stream/path overloads on both sides, async variants on .NET 10+, and an IZipArchive / IZipArchiveEntry wrapper so the abstraction extends through the archive itself rather than stopping at the zip boundary.

Production uses the real implementation

On RealFileSystem, every call forwards to the matching System.IO.Compression method, so production code keeps the performance of the base class library (BCL). Only MockFileSystem uses an in-memory implementation.

ZipFile​

fileSystem.ZipFile exposes the static methods of System.IO.Compression.ZipFile:

fileSystem.ZipFile
.CreateFromDirectory("artworks", "artworks.zip");

fileSystem.ZipFile
.ExtractToDirectory("artworks.zip", "vault");

using IZipArchive archive = fileSystem.ZipFile
.Open("artworks.zip", ZipArchiveMode.Update);

All overloads from the BCL are present, including the async variants on .NET 10+ (CreateFromDirectoryAsync, ExtractToDirectoryAsync, OpenAsync, OpenReadAsync).

ZipArchive​

fileSystem.ZipArchive is a factory for IZipArchive from a Stream (matches the new ZipArchive(...) constructors):

using FileSystemStream stream = fileSystem.File.OpenRead("artworks.zip");
using IZipArchive archive = fileSystem.ZipArchive
.New(stream, ZipArchiveMode.Read);

foreach (IZipArchiveEntry entry in archive.Entries)
{
Console.WriteLine(entry.FullName);
}

IZipArchive exposes Entries, Mode, CreateEntry, CreateEntryFromFile, GetEntry, ExtractToDirectory and, on .NET 10+, the async counterparts CreateEntryFromFileAsync and ExtractToDirectoryAsync. IZipArchiveEntry mirrors ZipArchiveEntry (Open, Delete, LastWriteTime, Length, CompressedLength, …).