Skip to content

Testing

Gustavo Viana edited this page Aug 7, 2026 · 4 revisions

Testing

Swap disk for memory — the same code that runs in production against LocalFileHub runs instantly in tests against MemoryFileHub.

The idea

Code under test depends on IFileHub / DirectoryEntry, not a driver. Production wires in LocalFileHub; tests wire in MemoryFileHub.

public class ReportService(IFileHub hub)
{
    public void Save(string name, string body)
        => hub.Root.CreateFile(name).SetText(body);
}
[Fact]
public void Save_writes_content()
{
    var hub = new MemoryFileHub();
    new ReportService(hub).Save("report.txt", "hello");

    Assert.Equal("hello", hub.Root.OpenFile("report.txt").ReadAllText());
}

No temp directory, no teardown. Fresh hub per test.

Preseed state

var hub = new MemoryFileHub();
hub.Root.CreateDirectory("inbox")
        .CreateFile("20260101.json").SetText("{\"id\":1}");

new InboxProcessor(hub).ProcessAll();

Assert.False(hub.Root.OpenDirectory("inbox").FileExists("20260101.json"));
Assert.True (hub.Root.OpenDirectory("processed").FileExists("20260101.json"));

Resolve from the SUT, not via closure

Let the system under test own its state — capturing the root from the DI factory gives you a handle to a different tree than the SUT constructs.

// ✅ resolve from the container
services.AddFileHub<IMemoryFileHub>(_ => new MemoryFileHub());

var provider = services.BuildServiceProvider();
var hub = provider.GetRequiredService<IMemoryFileHub>();

/* run SUT */
Assert.True(hub.Root.FileExists("report.txt"));

When you really need the disk

For code that shells out to a real process, point LocalFileHub at a per-test temp directory:

var tmp = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
try
{
    var hub = new LocalFileHub(tmp);
    /* ... */
}
finally { Directory.Delete(tmp, recursive: true); }

Read-only contracts

Wrap the hub before passing it to code that must not mutate — the test fails fast if the contract is broken.

var ro = hub.Root.OpenDirectory("config").AsReadOnly();
new ReadOnlyConsumer(ro);  // any CreateFile/Delete/Rename would throw

Do not mock the OCI driver

Mock your own service instead. If you need to exercise code that depends on IOracleObjectStorageFileHub, route it through IFileHub and inject a MemoryFileHub — the driver's own tests use a private IOciClient fake that is not meant to be reused externally.

Tips

  • New MemoryFileHub per test. No shared state.
  • Exists() after every mutation is the canonical presence assertion.
  • GetFiles / GetDirectories order is not guaranteed — don't depend on it.
  • Every async method takes a CancellationToken — wire up test cancellation where useful.

Clone this wiki locally