Testably.Abstractions
Testably.Abstractions provides injectable abstractions for the static parts of the .NET base class library (BCL), namely the file system, time and randomness, with feature-complete in-memory mocks for tests.
Inject. Mock. Test.
Depend on IFileSystem, ITimeSystem and IRandomSystem in production. Swap in the in-memory mocks for tests: deterministic, cross-platform, no temp folders or Thread.Sleep.
- File system
- Time system
- Random system
CatalogService.cs
public class CatalogService(IFileSystem fileSystem)
{
public void Export(string json)
{
fileSystem.Directory.CreateDirectory("exports");
fileSystem.File.WriteAllText("exports/catalog.json", json);
}
}
CatalogServiceTests.cs
[Test]
public async Task Export_WritesCatalogToExportsFolder()
{
MockFileSystem fileSystem = new();
CatalogService sut = new(fileSystem);
sut.Export("""{ "title": "Red Square" }""");
await That(fileSystem.File.ReadAllText("exports/catalog.json"))
.IsEqualTo("""{ "title": "Red Square" }""");
}
Exhibition.cs
public class Exhibition(ITimeSystem timeSystem, DateTime closesAt)
{
public bool IsOpen => timeSystem.DateTime.UtcNow < closesAt;
}
ExhibitionTests.cs
[Test]
public async Task IsOpen_ReturnsFalse_AfterClosingDate()
{
MockTimeSystem timeSystem = new(new DateTime(2026, 3, 1, 0, 0, 0, DateTimeKind.Utc));
Exhibition sut = new(timeSystem, closesAt: new DateTime(2026, 4, 15, 0, 0, 0, DateTimeKind.Utc));
timeSystem.TimeProvider.AdvanceBy(TimeSpan.FromDays(60));
await That(sut.IsOpen).IsFalse();
}
ArtworkOfTheDay.cs
public class ArtworkOfTheDay(IRandomSystem randomSystem, string[] artworks)
{
public string Pick() => artworks[randomSystem.Random.Shared.Next(artworks.Length)];
}
ArtworkOfTheDayTests.cs
[Test]
public async Task Pick_ReturnsTheArtworkAtTheRandomIndex()
{
MockRandomSystem randomSystem = new(RandomProvider.Generate(intGenerator: 1));
ArtworkOfTheDay sut = new(randomSystem, ["Red Square", "Blue Circles", "Yellow Lines"]);
await That(sut.Pick()).IsEqualTo("Blue Circles");
}
Why Testably.Abstractions?
Testably.Abstractions shares the IFileSystem interface with TestableIO.System.IO.Abstractions, so your production code does not change when you switch testing libraries. What you get on top:
- Advanced testing scenarios:
FileSystemWatcher,SafeFileHandle, multiple drives with size limits. - Cross-platform simulation: run a Linux file system on Windows (and vice versa) to exercise platform-specific behavior from a single CI job.
- A second abstraction layer for time and randomness.
See Migration from TestableIO if you are already using TestableIO.
Where to go next
- Getting started: install the packages and wire them up.
- A complete example: one gallery service tested with all three abstractions.
- File system, Time system, Random system: explore each abstraction.
- Companion libraries: ZIP, ACL and memory-mapped file helpers.
- aweXpect.Testably: expectations for the file system and time system mocks.