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
<PackageReference Include="SweetMock.Extensions.Caching" Version="1.2.48" />
<PackageVersion Include="SweetMock.Extensions.Caching" Version="1.2.48" />
<PackageReference Include="SweetMock.Extensions.Caching" />
paket add SweetMock.Extensions.Caching --version 1.2.48
#r "nuget: SweetMock.Extensions.Caching, 1.2.48"
#:package SweetMock.Extensions.Caching@1.2.48
#addin nuget:?package=SweetMock.Extensions.Caching&version=1.2.48
#tool nuget:?package=SweetMock.Extensions.Caching&version=1.2.48
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
AddandPopulatebefore the test runs - Time Control: Built-in
FakeTimeProviderlets 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+
HybridCacheabstraction 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.
Links
- Caching in .NET
- In-memory caching
- Distributed caching
- HybridCache library
- Testing time-dependent code with FakeTimeProvider
| Product | Versions 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. |
-
net10.0
- Microsoft.Extensions.Caching.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Caching.Memory (>= 10.0.0)
- Microsoft.Extensions.TimeProvider.Testing (>= 10.0.0)
- SweetMock (>= 1.2.48)
-
net8.0
- Microsoft.Extensions.Caching.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Caching.Memory (>= 10.0.0)
- Microsoft.Extensions.TimeProvider.Testing (>= 10.0.0)
- SweetMock (>= 1.2.48)
-
net9.0
- Microsoft.Extensions.Caching.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Caching.Memory (>= 10.0.0)
- Microsoft.Extensions.TimeProvider.Testing (>= 10.0.0)
- SweetMock (>= 1.2.48)
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 |