Mongo.Fakes.Server
0.9.0
See the version list below for details.
dotnet add package Mongo.Fakes.Server --version 0.9.0
NuGet\Install-Package Mongo.Fakes.Server -Version 0.9.0
<PackageReference Include="Mongo.Fakes.Server" Version="0.9.0" />
<PackageVersion Include="Mongo.Fakes.Server" Version="0.9.0" />
<PackageReference Include="Mongo.Fakes.Server" />
paket add Mongo.Fakes.Server --version 0.9.0
#r "nuget: Mongo.Fakes.Server, 0.9.0"
#:package Mongo.Fakes.Server@0.9.0
#addin nuget:?package=Mongo.Fakes.Server&version=0.9.0
#tool nuget:?package=Mongo.Fakes.Server&version=0.9.0
Mongo.Fakes
Wire-compatible MongoDB test doubles for the official MongoDB C# driver — no mongod process required.
Mongo.Fakes is two things sharing one filter engine:
Mongo.Fakes.Core— compiles MongoDB filter documents (BsonDocument) intoExpression<Func<BsonDocument, bool>>predicates. Stays entirely in BSON-land (BsonValuecomparisons, MongoDB type ordering, null-vs-missing semantics) instead of mapping to CLR types, so behavior matches real MongoDB.Mongo.Fakes.Server— an in-process MongoDB wire-protocol (OP_MSG) mock server that serves fixture data to a realIMongoClient/IMongoCollection, for tests that need to exercise actual driver code paths without standing up MongoDB.
Mongo.Fakes.Server uses Mongo.Fakes.Core as its filter engine, so operator semantics are
implemented once and shared by both the lightweight in-memory predicate mode and the
wire-protocol double.
When to Use Mongo.Fakes
| Aspect | Mongo.Fakes | EphemeralMongo | Testcontainers |
|---|---|---|---|
| Speed | ⚡ Very fast (in-process) | 🐢 Slower (real binary) | 🐢 Slower (Docker) |
| Setup | 0 ms, no dependencies | ~100 MB binary download | Docker required |
| Compatibility | 95% (queries, writes, aggregations) | 100% (full MongoDB) | 100% (full MongoDB) |
| Fixture Setup | Easy (JSON/BSON files) | Any method | Any method |
| Best For | Unit tests, CI speed, fixture validation | Integration tests needing 100% parity | Feature demos, complex scenarios |
| Test Database Reuse | ✓ Snapshot real data as BSON | ✓ Full compatibility | ✓ Full compatibility |
Choose Mongo.Fakes if: Your tests don't use unsupported operators, you want instant startup, and you're testing driver integration paths rather than advanced MongoDB features.
Choose EphemeralMongo/Testcontainers if: You need 100% MongoDB compatibility or are testing features like transactions, geospatial queries, or complex aggregations.
Status
Early scaffold — see docs/SPEC.md for the design specification and
current scope.
Packages
| Package | Purpose |
|---|---|
Mongo.Fakes.Core |
Filter compiler: BsonDocument filter → LINQ predicate |
Mongo.Fakes.Server |
Wire-protocol test double server backed by fixture files |
Usage
Loading Fixture Data
From Local JSON/BSON Files
Create a folder structure matching your database layout:
Fixtures/
myapp/
users.json
products.json
other_db/
items.json
Each line in the JSON files is a BSON document:
{"_id": 1, "name": "Alice", "email": "alice@example.com"}
{"_id": 2, "name": "Bob", "email": "bob@example.com"}
From MongoDB Dump (mongodump)
Import real exported data using mongodump:
mongodump --uri "mongodb://prod-server/myapp" --out ./dump
This creates a structure like: dump/myapp/users.bson, dump/myapp/products.bson, etc.
Then load it in your tests:
var backend = new BsonFileBackend(Path.Combine(
Directory.GetCurrentDirectory(), "dump"), loadFromMongoDump: true);
The server automatically handles both .json and .bson files, making it easy to snapshot
real production data as test fixtures.
Integration Tests with xUnit
Use MongoFakeServer with xUnit's IAsyncLifetime to manage server lifecycle in tests:
using MongoDB.Driver;
using Xunit;
public class UserServiceTests : IAsyncLifetime
{
private MongoFakeServer _server;
private IMongoClient _client;
public async Task InitializeAsync()
{
// Start the fake server on an auto-assigned port
var backend = new BsonFileBackend(Path.Combine(
Directory.GetCurrentDirectory(), "Fixtures"));
_server = new MongoFakeServer(backend, port: 0);
await _server.StartAsync();
// Use the connection string just like EphemeralMongo
_client = new MongoClient(_server.ConnectionString);
}
public async Task DisposeAsync()
{
_client?.Dispose();
if (_server != null)
await _server.DisposeAsync();
}
[Fact]
public async Task CreateUser_Should_Insert_Document()
{
var db = _client.GetDatabase("myapp");
var users = db.GetCollection<BsonDocument>("users");
var newUser = new BsonDocument
{
{ "name", "Alice" },
{ "email", "alice@example.com" },
{ "age", 30 }
};
await users.InsertOneAsync(newUser);
var found = await users.Find(
Builders<BsonDocument>.Filter.Eq("email", "alice@example.com")
).FirstOrDefaultAsync();
Assert.NotNull(found);
Assert.Equal("Alice", found["name"].AsString);
}
[Fact]
public async Task QueryWithFilter_Should_Return_Matching_Documents()
{
var db = _client.GetDatabase("myapp");
var products = db.GetCollection<BsonDocument>("products");
await products.InsertManyAsync(new[]
{
new BsonDocument { { "name", "Widget" }, { "price", 9.99 }, { "category", "tools" } },
new BsonDocument { { "name", "Gadget" }, { "price", 19.99 }, { "category", "electronics" } },
new BsonDocument { { "name", "Tool" }, { "price", 14.99 }, { "category", "tools" } }
});
var filter = Builders<BsonDocument>.Filter.Eq("category", "tools");
var results = await products.Find(filter).ToListAsync();
Assert.Equal(2, results.Count);
Assert.All(results, doc => Assert.Equal("tools", doc["category"].AsString));
}
}
The MongoFakeServer provides:
ConnectionStringproperty for connecting withMongoClient(no process management needed)Portproperty (auto-assigned if you passport: 0)StartAsync()/DisposeAsync()for lifecycle management- Semantics compatible with EphemeralMongo's
MongoRunner
GridFS Support
GridFS operations (file upload/download via MongoDB.Driver.GridFSBucket) are fully supported without any special configuration:
var db = _client.GetDatabase("myapp");
var bucket = new GridFSBucket(db);
// Upload a file
var fileContent = Encoding.UTF8.GetBytes("Hello, World!");
var fileId = await bucket.UploadFromBytesAsync("hello.txt", fileContent);
// Download the file
var downloaded = await bucket.DownloadAsBytesAsync(fileId);
// Also supports streaming operations
using (var uploadStream = await bucket.OpenUploadStreamAsync("data.bin"))
{
await uploadStream.WriteAsync(largeData, 0, largeData.Length);
}
GridFS works transparently by using the existing insert, find, update, and delete command support — no bucket-specific code is needed. Files spanning multiple chunks are handled automatically.
Performance: Copy-on-Write (CoW) Fixture Isolation
For test suites with hundreds of test fixtures sharing the same baseline data, Mongo.Fakes implements per-document copy-on-write (CoW) to minimize memory overhead:
- Baseline data (loaded from fixture files) is shared across all test fixtures
- Per-fixture mutations are tracked separately — when a test modifies a document, only the changed version is copied to heap
- Unmodified documents reference the original baseline (zero memory overhead)
This enables thousands of concurrent test fixtures to maintain isolation without the memory cost of cloning 100MB+ baselines per fixture.
Typical scenario:
- Baseline: 100 MB shared across all fixtures
- Test Class A: inserts 5 docs, updates 3 → ~50 KB mutations
- Test Class B: deletes 2 docs → ~20 KB mutations
- Total memory: 100 MB + 70 KB (instead of 200 MB for naive cloning)
For tests that don't mutate data, the footprint is the baseline size alone — perfect for read-heavy test suites.
How CoW Works: DocumentSnapshot
Internally, Mongo.Fakes wraps each document in a DocumentSnapshot that tracks baseline vs. mutated state:
internal sealed class DocumentSnapshot
{
public BsonDocument Original { get; } // Shared baseline
public BsonDocument? Mutated { get; set; } // Test-specific copy
public BsonDocument Current => Mutated ?? Original;
public bool IsDirty => Mutated != null;
}
Lifecycle:
- Load — Document is loaded from fixture file into
Original(shared) - Read — Tests read via
Current(points toOriginal) - Mutate — When modified,
Mutatedis populated with a copy - Track — Future reads use
Mutatedfor this snapshot only; other fixtures still seeOriginal - Cleanup — When test fixture is torn down, only
Mutatedis discarded;Originalpersists for next test
Tracking Snapshots with Instance Names
For complex test fixtures with many documents, you can name snapshots to track their state during debugging:
// Create a named snapshot (useful for logging/debugging)
var userSnapshot = new DocumentSnapshot(userDoc, instanceName: "user_alice");
// Later, you can log the state
Console.WriteLine(userSnapshot.GetDebugInfo());
// Output: Snapshot[user_alice] (id=1, state=baseline)
// After mutation
userSnapshot.Mutated = new BsonDocument(userSnapshot.Original) { { "email", "new@example.com" } };
Console.WriteLine(userSnapshot.GetDebugInfo());
// Output: Snapshot[user_alice] (id=1, state=mutated)
Naming Patterns:
| Pattern | Example | Use Case |
|---|---|---|
| Collection + ID | users_42 |
Default for tracking by collection |
| Fixture + Collection + Seq | test_fixture_users_1 |
Multiple fixtures, explicit ordering |
| Human-readable | admin_user_alice |
Debugging & test reports |
| Fixture name + field | order_123_status |
Tracking specific field changes |
Example with a SnapshotRegistry:
var registry = new DocumentSnapshotRegistry();
// Register and track snapshots
registry.Register("user_alice", new DocumentSnapshot(userDoc, "user_alice"));
registry.Register("user_bob", new DocumentSnapshot(bobDoc, "user_bob"));
registry.Register("product_laptop", new DocumentSnapshot(productDoc, "product_laptop"));
// Modify one
var alice = registry.Get("user_alice");
alice.Mutated = new BsonDocument(alice.Original) { { "status", "active" } };
// Get diagnostic summary
Console.WriteLine(registry.GetSummary());
/* Output:
Snapshot[user_alice] (id=1, state=mutated)
Snapshot[user_bob] (id=2, state=baseline)
Snapshot[product_laptop] (id=101, state=baseline)
*/
// Query dirty (modified) snapshots only
var dirtySnapshots = registry.GetDirty();
Console.WriteLine($"Modified snapshots: {dirtySnapshots.Count()}");
// Output: Modified snapshots: 1
Memory Visualization:
Before any modifications:
┌──────────────────────────────┐
│ Shared Baseline (100 MB) │ ← All 3 test fixtures reference this
│ - 50,000 user documents │
│ - 5,000 product documents │
│ - Memory: 100 MB │
└──────────────────────────────┘
After Test A modifies 3 users:
┌──────────────────────────────┐
│ Shared Baseline (100 MB) │ ← Tests B & C still use original
│ - 50,000 user documents │
│ - 5,000 product documents │
└──────────────────────────────┘
↓ (Test A mutations)
┌────────────────┐
│ Test A Copy │ ← Only 3 modified users copied
│ (50 KB) │
└────────────────┘
Total memory: 100 MB + 50 KB (vs. 300 MB if all cloned)
Building
dotnet build
dotnet test
Targets net8.0 and net10.0.
License
| 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 was computed. 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
- Mongo.Fakes.Core (>= 0.9.0)
- MongoDB.Bson (>= 3.11.0)
-
net8.0
- Mongo.Fakes.Core (>= 0.9.0)
- MongoDB.Bson (>= 3.11.0)
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 |
|---|---|---|
| 0.11.2 | 52 | 9/14/2026 |
| 0.11.1 | 48 | 9/13/2026 |
| 0.11.0 | 109 | 9/5/2026 |
| 0.10.0 | 126 | 8/29/2026 |
| 0.9.0 | 96 | 8/27/2026 |
| 0.8.1 | 107 | 8/27/2026 |
| 0.8.0 | 106 | 8/26/2026 |
| 0.7.7 | 102 | 8/26/2026 |
| 0.7.6 | 93 | 8/26/2026 |
| 0.7.5 | 102 | 8/26/2026 |
| 0.7.4 | 106 | 8/26/2026 |
| 0.7.3 | 110 | 8/25/2026 |
| 0.7.2 | 107 | 8/25/2026 |
| 0.7.1 | 106 | 8/25/2026 |
| 0.7.0 | 104 | 8/25/2026 |
| 0.6.0 | 116 | 8/25/2026 |
| 0.5.0 | 95 | 8/25/2026 |
| 0.4.0 | 99 | 8/25/2026 |
| 0.3.1 | 101 | 8/25/2026 |
| 0.1.0 | 111 | 8/24/2026 |