testableio/system.io.abstractions

repository·main·Indexed 23 days ago

https://github.com/testableio/system.io.abstractions

A set of interfaces that wrap standard .NET System.IO APIs to make file system operations injectable and testable. It provides the IFileSystem interface and a default FileSystem implementation, allowing developers to simulate file system interactions using MockFileSystem from the TestableIO.System.IO.Abstractions.TestingHelpers package without relying on the actual disk.

Tokens
1.4K
Snippets
4
Records
7
Agent score
32%

What's inside testableio/system.io.abstractions

  1. Compare TestableIO vs Testably.Abstractions

    main

    Both projects share the same interfaces, meaning you can switch between them without changing your production code.

    Use TestableIO.System.IO.Abstractions if:

    • You need basic file system mocking.
    • You want to directly manipulate stored entities like MockFileData or MockDirectoryData.
    • You have an established codebase already using TestableIO.

    Use Testably.Abstractions if:

    • You need advanced scenarios (e.g., FileSystemWatcher, SafeFileHandles, multiple drives).
    • You need additional abstractions like ITimeSystem or IRandomSystem.
    • You require cross-platform file system simulation (Linux, MacOS, Windows).
    • You want active development and new features.
  2. How IFileSystem and FileSystem work together

    main

    The core of the library revolves around the IFileSystem interface and its default implementation, FileSystem. Instead of calling static methods from the standard .NET System.IO.File or System.IO.Directory classes, you should use the injectable IFileSystem instance. This allows you to swap the real file system (which calls System.IO) with a mock or a testable implementation during unit testing.

    In production, you typically use the default FileSystem implementation, while in tests, you inject a mock or a helper-based file system.

  3. Install TestableIO.System.IO.Abstractions.Wrappers

    main

    To use the abstractions in your production code, install the wrappers package. While this package is also published under the name System.IO.Abstractions on NuGet, it is recommended to use the TestableIO. prefix to distinguish it from official .NET packages.

    dotnet add package TestableIO.System.IO.Abstractions.Wrappers
  4. Migrate from TestableIO to Testably.Abstractions

    main

    To migrate, you only need to update your test projects. Your production code using IFileSystem remains unchanged.

    1. Update NuGet references in your test projects:
    <!-- Remove -->
    <PackageReference Include="TestableIO.System.IO.Abstractions.TestingHelpers" />
    <!-- Add -->
    <PackageReference Include="Testably.Abstractions.Testing" />
    1. Update test code to use the new MockFileSystem API:

    Before (TestableIO):

    var fileSystem = new MockFileSystem();
    fileSystem.AddDirectory("some-directory");
    fileSystem.AddFile("some-file.txt", new MockFileData("content"));

    After (Testably):

    var fileSystem = new MockFileSystem();
    fileSystem.Directory.CreateDirectory("some-directory");
    fileSystem.File.WriteAllText("some-file.txt", "content");
    // OR using fluent initialization:
    fileSystem.Initialize()
        .WithSubdirectory("some-directory")
        .WithFile("some-file.txt").Which(f => f
            .HasStringContent("content"));
  5. Mock top-level APIs using interfaces

    main

    Since version 4.0, the library exposes interfaces (like IFileSystemWatcher and IFile) instead of abstract base classes. This allows for complete mocking of the file system using standard mocking frameworks like Mockolate.

    [Test]
    public void Test1()
    {
        var watcher = Mock.Create<IFileSystemWatcher>();
        var file = Mock.Create<IFile>();
    
        file.SetupMock.Method.Exists(It.IsAny<string>()).Returns(true);
        file.SetupMock.Method.ReadAllText(It.IsAny<string>()).Throws<OutOfMemoryException>();
    
        var unitUnderTest = new SomeClassUsingFileSystemWatcher(watcher, file);
    
        // ...
    }
  6. Use MockFileSystem for basic testing scenarios

    main

    The TestableIO.System.IO.Abstractions.TestingHelpers package provides MockFileSystem, which allows you to simulate a file system in memory without mocking every individual call. You can initialize it with a dictionary of paths and MockFileData objects to represent files and directories.

    // Install: dotnet add package TestableIO.System.IO.Abstractions.TestingHelpers
    
    [Test]
    public void MyComponent_Validate_ShouldThrowNotSupportedExceptionIfTestingIsNotAwesome()
    {
        // Arrange
        var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
        {
            { @"c:\myfile.txt", new MockFileData("Testing is meh.") },
            { @"c:\demo\jQuery.js", new MockFileData("some js") },
            { @"c:\demo\image.gif", new MockFileData(new byte[] { 0x12, 0x34, 0x56, 0xd2 }) }
        });
        var component = new MyComponent(fileSystem);
    
        // Act & Assert
        // ...
    }
  7. Cast .NET Framework types to testable wrappers

    main

    If you are interacting with APIs that return standard .NET types like FileInfo, you can cast them to FileInfoBase to make them testable within the library's ecosystem.

    FileInfo SomeApiMethodThatReturnsFileInfo()
    {
        return new FileInfo("a");
    }
    
    void MyFancyMethod()
    {
        var testableFileInfo = (FileInfoBase)SomeApiMethodThatReturnsFileInfo();
        // ...
    }