SweetMock.Extensions.Caching 1.2.48

dotnet add package SweetMock.Extensions.Caching --version 1.2.48
                    
NuGet\Install-Package SweetMock.Extensions.Caching -Version 1.2.48
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SweetMock.Extensions.Caching" Version="1.2.48" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SweetMock.Extensions.Caching" Version="1.2.48" />
                    
Directory.Packages.props
<PackageReference Include="SweetMock.Extensions.Caching" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add SweetMock.Extensions.Caching --version 1.2.48
                    
#r "nuget: SweetMock.Extensions.Caching, 1.2.48"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package SweetMock.Extensions.Caching@1.2.48
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=SweetMock.Extensions.Caching&version=1.2.48
                    
Install as a Cake Addin
#tool nuget:?package=SweetMock.Extensions.Caching&version=1.2.48
                    
Install as a Cake Tool

SweetMock.Extensions.Caching

A comprehensive extension library for mocking IMemoryCache, IDistributedCache, and HybridCache in .NET tests using the SweetMock framework.

Features

  • Mock IMemoryCache: Fully functional in-memory cache mock backed by a real MemoryCache
  • Mock IDistributedCache: Distributed cache mock backed by MemoryDistributedCache
  • Mock HybridCache: Hybrid cache mock with tag support and cache hit/miss tracking
  • Pre-populate Entries: Seed the cache with values via Add and Populate before the test runs
  • Time Control: Built-in FakeTimeProvider lets you test cache expiration without waiting
  • Call Logging: Inspect every cache interaction, including cache hits, misses, additions, and removals

Installation

dotnet add package SweetMock.Extensions.Caching

Dependencies

Please note that this component has dependencies to Microsoft.Extensions.Caching.Abstractions, Microsoft.Extensions.Caching.Memory and Microsoft.Extensions.TimeProvider.Testing.

Quick Start

IMemoryCache Fixture

[Fixture<MyService>]
public class MyServiceTests
{
    [Fact]
    public void Service_ReadsFromCache()
    {
        // Arrange
        var sut = Mock.IMemoryCache(config => config.Add("The answer", 42));

        // Act
        var found = sut.TryGetValue("The answer", out var actual);

        // Assert
        Assert.True(found);
        Assert.Equal(42, actual);
    }
}

public class MyService(IMemoryCache cache)
{
    public bool TryGetAnswer(out object? answer) =>
        cache.TryGetValue("The answer", out answer);
}

IMemoryCache

The IMemoryCache mock is backed by a real MemoryCache instance, so all standard cache semantics (entry options, expiration, eviction) behave as expected.

Pre-populating Entries

// Add individual entries
var sut = Mock.IMemoryCache(config => config.Add("key", "value"));

// Add with entry options
var sut = Mock.IMemoryCache(config =>
    config.Add("key", "value", o => o.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1))
);

// Populate from a dictionary
var sut = Mock.IMemoryCache(config =>
    config.Populate(new Dictionary<object, object>
    {
        { "The answer", 42 },
        { "Not the answer", 45 }
    })
);

Writing From the System Under Test

Entries added in the configuration can be overwritten just like in a real cache:

var sut = Mock.IMemoryCache(out var config);
config.Add("The answer", 45);

sut.Set("The answer", 42); // Overwrites the configured value

sut.TryGetValue("The answer", out var actual);
Assert.Equal(42, actual);

IDistributedCache

The IDistributedCache mock is backed by MemoryDistributedCache and works with byte[] values like the real interface. Both synchronous and asynchronous members (Get/GetAsync, Set/SetAsync, Refresh/RefreshAsync, Remove/RemoveAsync) are wired up.

// Add individual entries
var sut = Mock.IDistributedCache(config => config.Add("The answer", [42]));

// Add with entry options
var sut = Mock.IDistributedCache(config =>
    config.Add("The answer", [42], o => o.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1))
);

// Populate from a dictionary
var sut = Mock.IDistributedCache(config =>
    config.Populate(new Dictionary<string, byte[]>
    {
        { "The answer", [42] },
        { "Not the answer", [45] }
    })
);

var actual = sut.Get("The answer");
Assert.Equal([42], actual);

HybridCache

The HybridCache mock supports GetOrCreateAsync, SetAsync, RemoveAsync, and RemoveByTagAsync, including tag-based invalidation.

Pre-populating Entries

// Add individual entries
var sut = Mock.HybridCache(out var config);
config.Add("The answer", 42);

// Add with hybrid cache entry options
config.Add("The answer", 42, new() { LocalCacheExpiration = TimeSpan.FromMinutes(1) });

// Populate from a dictionary
config.Populate(new() { { "The answer", 42 }, { "Not the answer", 45 } });

GetOrCreateAsync

Configured entries are returned instead of invoking the factory:

var sut = Mock.HybridCache(out var config);
config.Add("The answer", 42);

var actual = await sut.GetOrCreateAsync("The answer", _ => new ValueTask<int>(45));

Assert.Equal(42, actual); // Factory was not called

Tag-based Removal

await cache.SetAsync("The answer", 42, options, ["HHGTTG"]);

// Removes all entries tagged with "HHGTTG"
await cache.RemoveByTagAsync(["HHGTTG"]);

Testing Cache Expiration

All three mocks use a built-in FakeTimeProvider, so expiration can be tested without real delays via the Time configuration method:

var sut = Mock.IMemoryCache(out var config);
config.Add("The answer", 42, o => o.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1));

// Entry is available before expiration
Assert.True(sut.TryGetValue("The answer", out _));

// Advance time past the expiration
config.Time(t => t.Advance(TimeSpan.FromMinutes(2)));

// Entry has expired
Assert.False(sut.TryGetValue("The answer", out _));

The same pattern works for IDistributedCache and HybridCache:

var sut = Mock.HybridCache(out var config);
config.Add("The answer", 42, new() { LocalCacheExpiration = TimeSpan.FromMinutes(1) });

config.Time(t => t.Advance(TimeSpan.FromMinutes(2)));

// The factory is now invoked because the entry expired
var actual = await sut.GetOrCreateAsync("The answer", _ => new ValueTask<int>(45));
Assert.Equal(45, actual);

Inspecting Mock Calls

Track and verify cache interactions made during tests:

MockOf_IMemoryCache.Logs logs = null!;
var sut = Mock.IMemoryCache(config =>
    config
        .Add("The answer", 42)
        .GetCallLogs(out logs)
);

sut.TryGetValue("The answer", out _);

// Entries added via config do not show up as calls
Assert.Empty(logs.CreateEntry());

HybridCache Hit/Miss Tracking

The HybridCache mock logs additional cache events beyond regular member calls:

var sut = Mock.HybridCache(out var config);
config.Add("The answer", 42);
config.GetCallLogs(out var logs);

await sut.GetOrCreateAsync("The answer", _ => new ValueTask<int>(45));
await sut.GetOrCreateAsync("Unknown", _ => new ValueTask<int>(45));

// Inspect cache events
var hits = logs.CacheHit();       // "The answer" was found
var misses = logs.CacheMiss();    // "Unknown" was not found
var additions = logs.CacheAdd();  // "Unknown" was added by the factory
var removals = logs.CacheRemove();

// Filter by key
var answerHits = logs.CacheHit(hit => hit.Key == "The answer");

Using With Fixtures

The mocks integrate with SweetMock fixtures for testing classes that take a cache as a dependency:

[Fixture<CachedRepository>]
public class CachedRepositoryTests(ITestOutputHelper output)
{
    [Fact]
    public async Task Repository_UsesCachedValue()
    {
        // Arrange
        var (sut, log) = Fixture.CachedRepository(config =>
            config.cache.Populate(new() { { "key", "value" } },
                new() { LocalCacheExpiration = TimeSpan.FromMinutes(1) })
        );

        // Act
        await sut.GetValue("key");

        // Assert / Diagnose
        output.Dump(log);
    }
}

public class CachedRepository(HybridCache cache)
{
    public async Task<string> GetValue(string key) =>
        await cache.GetOrCreateAsync(key, _ => ValueTask.FromResult("from source"));
}

API Reference

MockOf_IMemoryCache.MockConfig

config.Add(object key, object value, Action<ICacheEntry>? options = null);
config.Populate(IDictionary<object, object> source, Action<ICacheEntry>? options = null);
config.Time(Action<FakeTimeProvider> modify);

MockOf_IDistributedCache.MockConfig

config.Add(string key, byte[] value, Action<DistributedCacheEntryOptions>? options = null);
config.Populate(IDictionary<string, byte[]> source, Action<DistributedCacheEntryOptions>? options = null);
config.Time(Action<FakeTimeProvider> modify);

MockOf_HybridCache.MockConfig

config.Add(string key, object value, HybridCacheEntryOptions? options = null);
config.Populate(Dictionary<string, object> source, HybridCacheEntryOptions? options = null);
config.Time(Action<FakeTimeProvider> modify);

HybridCache Log Entries

logs.CacheHit(filter);     // Entries found in the cache
logs.CacheMiss(filter);    // Entries not found in the cache
logs.CacheAdd(filter);     // Entries added to the cache
logs.CacheRemove(filter);  // Entries removed from the cache

Choosing the Right Cache

  • IMemoryCache: Use for testing code that caches objects in process memory
  • IDistributedCache: Use for testing code that works with serialized (byte[]) cache values, e.g. Redis or SQL Server backed caches
  • HybridCache: Use for testing code built on the .NET 9+ HybridCache abstraction with multi-tier caching and tag-based invalidation

Requirements

  • .NET 8.0, 9.0, or 10.0
  • SweetMock
  • Microsoft.Extensions.Caching.Abstractions
  • Microsoft.Extensions.Caching.Memory
  • Microsoft.Extensions.TimeProvider.Testing

License

See the main SweetMock repository for license information.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.48 100 9/22/2026
1.1.45 123 9/11/2026
1.0.42 150 7/19/2026
1.0.42-RC3 115 7/11/2026