Repository navigation
Getting Started
CosmosDB.InMemoryEmulator is an in-process fake for the Azure Cosmos DB .NET SDK. It intercepts SDK HTTP requests via FakeCosmosHandler, giving you full SDK fidelity — LINQ, SQL queries, change feed, patches, batches, and more — with zero external dependencies and zero production code changes. No Docker, no emulator process, no network.
This page covers installation and your first test. For help choosing between the three integration approaches, see Choosing Your Approach. For full setup details — Dependency Injection patterns, direct instantiation, and configuration options — see the Setup Guide.
Add the emulator package to your test project:
dotnet add package CosmosDB.InMemoryEmulator
| Requirement | Version |
|---|---|
| .NET | 8.0+ |
| Microsoft.Azure.Cosmos | 3.58.0+ |
The recommended approach uses UseInMemoryCosmosDB() to replace your real Cosmos registrations with in-memory equivalents. No production code changes required.
// Program.cs / Startup.cs
services.AddSingleton<CosmosClient>(sp =>
new CosmosClient(configuration["CosmosDb:ConnectionString"]));
services.AddSingleton<Container>(sp =>
{
var client = sp.GetRequiredService<CosmosClient>();
return client.GetContainer("MyDatabase", "orders");
});// OrderRepository.cs — depends on Container via Dependency Injection
public class OrderRepository
{
private readonly Container _container;
public OrderRepository(Container container) => _container = container;
public async Task<Order> CreateAsync(Order order)
{
var response = await _container.CreateItemAsync(order, new PartitionKey(order.CustomerId));
return response.Resource;
}
public async Task<Order?> GetAsync(string id, string customerId)
{
try
{
var response = await _container.ReadItemAsync<Order>(id, new PartitionKey(customerId));
return response.Resource;
}
catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
{
return null;
}
}
}using CosmosDB.InMemoryEmulator;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
public class MyAppFactory : WebApplicationFactory<Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services =>
{
services.UseInMemoryCosmosDB();
});
}
}That single line replaces the real CosmosClient with a real CosmosClient backed by FakeCosmosHandler — all HTTP requests are intercepted in-process. Your existing Container factory registrations are preserved — they resolve against the new client and route through in-memory storage automatically.
✅ LINQ
.ToFeedIterator()just works. BecauseUseInMemoryCosmosDB()usesFakeCosmosHandlerinternally, the SDK’s own LINQ provider translates your LINQ expressions into Cosmos DB SQL. No production code changes or extra packages needed.
⚠️ Serialization matters. The Cosmos SDK serializes your C# objects using Newtonsoft.Json by default. If your model properties use PascalCase (e.g.Id), you need[JsonProperty("id")]attributes fromNewtonsoft.Json— otherwise creates/upserts will fail with "Item must have an 'id' property". See Troubleshooting for details.
public class OrderTests : IClassFixture<MyAppFactory>
{
private readonly HttpClient _client;
public OrderTests(MyAppFactory factory) => _client = factory.CreateClient();
[Fact]
public async Task CreateOrder_ReturnsCreatedOrder()
{
var order = new { customerId = "cust-1", product = "Widget", price = 9.99 };
var response = await _client.PostAsJsonAsync("/api/orders", order);
response.StatusCode.Should().Be(HttpStatusCode.Created);
}
}By default, auto-detected containers use /id as the partition key path. If your tests need a specific path:
services.UseInMemoryCosmosDB(options => options
.AddContainer("orders", "/customerId"));See the full Setup Guide for multiple containers, typed clients, custom factory interfaces, and more. For a comparison of all integration approaches, see Choosing Your Approach.
InMemoryCosmos provides full SDK fidelity with the simplest setup — a real CosmosClient backed by FakeCosmosHandler, plus built-in test setup and fault injection. This is the Direct Instantiation approach:
// Single container — one-liner
using var cosmos = InMemoryCosmos.Create("orders", "/customerId");
// Use the real SDK Container
await cosmos.Container.CreateItemAsync(
new Order { Id = "1", CustomerId = "cust-1", Product = "Widget" },
new PartitionKey("cust-1"));
// Test setup (UDFs, stored procs, seeding)
cosmos.SetupContainer().RegisterUdf("discount", args => (double)args[0] * 0.9);
// Fault injection (429 throttling, 503 errors)
cosmos.Handler.FaultInjector = req => new HttpResponseMessage((HttpStatusCode)429);Multi-container:
using var cosmos = InMemoryCosmos.Builder()
.AddContainer("orders", "/customerId")
.AddContainer("products", "/categoryId")
.Build();
var orders = cosmos.Containers["orders"];
var products = cosmos.Containers["products"];Multi-database: see Multi-Database.
Both quick starts above show creating data within individual tests. For shared seed data, Dependency Injection callbacks, JSON snapshots, and automatic state persistence, see Seeding Data.
If your codebase has a hand-rolled in-memory Cosmos fake, the emulator can replace it entirely. This section explains what you can delete, what to watch for, and how to approach the migration.
| Custom fake component | Why it's no longer needed |
|---|---|
Custom InMemoryContainer / FakeCosmosDb class |
Replaced by InMemoryContainer
|
Hardcoded SQL query matching (e.g. if (query.Contains("SELECT * FROM c WHERE ..."))) |
The emulator has a full SQL parser (CosmosSqlParser) that executes arbitrary Cosmos SQL against in-memory data |
Manual PatchItemAsync with JObject manipulation |
The emulator supports all patch operation types (Set, Replace, Add, Remove, Increment, Move) including deep nested paths and conditional patches |
Custom TransactionalBatch implementation |
The emulator supports full transactional batch semantics including atomicity, rollback on failure, and 100-operation limit |
FeedIterator<T> wrappers / mock iterators |
FakeCosmosHandler handles FeedIterator natively through the SDK pipeline |
| Partition key validation stubs | The emulator validates partition keys including hierarchical/composite keys |
| ETag / concurrency simulation | The emulator supports IfMatchEtag, IfNoneMatchEtag, and wildcard *
|
-
Add the NuGet package to your test project:
dotnet add package CosmosDB.InMemoryEmulator - Choose your integration approach: see Choosing Your Approach for a decision flowchart. If you have a custom factory interface, see Pattern 5
- Create your fixture: map each container name to its partition key path(s) — see the Pattern 5 example for a multi-container setup with mixed single and hierarchical partition keys
-
Replace your Dependency Injection registration: in
ConfigureTestServices, replace your custom fake factory with the new fixture - Delete the custom fake: remove the old in-memory container, custom query handlers, batch stubs, etc.
-
Run tests: most should pass immediately. If not, check:
- Serialization settings match production (see Troubleshooting)
- Partition key paths match your container definitions
- SQL queries use standard Cosmos SQL (not custom query IDs)
- Custom query IDs vs real SQL: if your old fake used an enum or string ID to select a hardcoded LINQ query, the emulator's SQL parser handles real Cosmos SQL. Verify the actual SQL your production code generates.
-
Serialization differences: custom fakes often use direct object storage (no serialization round-trip). The emulator with
FakeCosmosHandlerexercises the real SDK serialization pipeline, which may expose previously-hidden bugs in your models (missing[JsonProperty]attributes, wrong casing, etc.). This is a feature, not a bug. - Partition key path casing: custom fakes may have been lenient about partition key path casing. The emulator validates paths as configured.
-
idproperty: the emulator requires a JSON"id"property on every document, just like real Cosmos DB. If your models useId(PascalCase), ensure your serializer is configured for camelCase or add[JsonProperty("id")].
-
Choosing Your Approach — compare Dependency Injection extensions, direct usage, and
FakeCosmosHandler; decision flowchart -
How It Works — how
FakeCosmosHandlerintercepts the SDK pipeline and compares to other approaches - Setup Guide — full Dependency Injection patterns (typed clients, custom factories, multi-container)
-
Unit Testing —
InMemoryCosmos.Create()usage without Dependency Injection - Seeding Data — Dependency Injection callbacks, SDK methods, snapshots, bulk seeding, state persistence
-
How Does ToFeedIterator() Work? — how
.ToFeedIterator()works withFakeCosmosHandler - SQL Queries — full reference for the built-in query engine (125+ functions)
- Features — patch, batches, change feed, ETags, TTL, stored procs, state persistence, PITR
- API Reference — complete class and method reference
- Known Limitations — behavioural differences from real Cosmos DB
- Troubleshooting — common errors and how to fix them
Getting Started
Integration & Dependency Injection
Data Management
Reference
Help