Repository navigation
Seeding Data
Guide to populating the in-memory emulator with test data. Covers all Dependency Injection patterns, direct instantiation, shared fixtures, bulk seeding, state persistence, and automatic state persistence between test runs.
There are five main approaches to seeding data, each suited to different situations:
| Approach | When to use |
|---|---|
InMemoryCosmos callbacks (configureContainer / ImportState() / ImportStateFromFile()) |
Seed data with InMemoryCosmos — simplest for non-Dependency Injection tests |
Dependency Injection callbacks (OnHandlerCreated / OnContainerCreated) |
Seed during service registration — data is ready before the first test runs |
SDK methods (CreateItemAsync / UpsertItemAsync) |
Seed through the real SDK pipeline — highest fidelity, per-test data |
State persistence (ImportState / ImportStateFromFile) |
Restore a pre-built dataset from JSON — fast, repeatable, shareable |
| Direct container access (resolve from Dependency Injection or capture reference) | Seed after the host is built but before tests run |
Automatic persistence (StatePersistenceDirectory) |
Automatically save/restore container state between test runs — see State Persistence |
All approaches work with every Dependency Injection pattern and with direct instantiation.
The configureContainer callback provides an IContainerTestSetup — use it for
state import, stored procedure/UDF/trigger registration, and configuration:
using var cosmos = InMemoryCosmos.Create("orders", "/customerId",
configureContainer: setup =>
{
setup.ImportState("""
{
"items": [
{ "id": "order-1", "customerId": "cust-1", "total": 99.99 }
]
}
""");
});For item-by-item seeding via SDK methods, seed after creation instead:
using var cosmos = InMemoryCosmos.Create("orders", "/customerId");
// Import a JSON snapshot
cosmos.ImportState(File.ReadAllText("seed-data.json"));
// Or seed via the SDK
await cosmos.Container.CreateItemAsync(
new { id = "order-1", customerId = "cust-1", total = 99.99 },
new PartitionKey("cust-1"));using var cosmos = InMemoryCosmos.Builder()
.AddContainer("orders", "/customerId", setup =>
{
setup.ImportState("""{"items":[{"id":"1","customerId":"c1","product":"Widget"}]}""");
})
.AddContainer("products", "/categoryId")
.Build();
// Seed products after build
cosmos.ImportState(productsSeedJson, "products");The Dependency Injection extension methods expose callbacks that fire during service registration. These are the simplest
way to seed data when using ConfigureTestServices.
OnHandlerCreated gives you the FakeCosmosHandler for each container. Access the backing
container via handler.BackingContainer and seed using standard SDK methods:
builder.ConfigureTestServices(services =>
{
services.UseInMemoryCosmosDB(options =>
{
options.AddContainer("orders", "/customerId");
options.OnHandlerCreated = (containerName, handler) =>
{
var backing = handler.BackingContainer;
backing.CreateItemAsync(
new { id = "order-1", customerId = "cust-1", total = 99.99 },
new PartitionKey("cust-1")).Wait();
backing.CreateItemAsync(
new { id = "order-2", customerId = "cust-2", total = 149.50 },
new PartitionKey("cust-2")).Wait();
};
});
});Note: The callback runs synchronously during Dependency Injection registration, so use
.Wait()or.GetAwaiter().GetResult()for async methods. This is safe because the in-memory container never blocks.
OnContainerCreated gives you an IContainerTestSetup for state management and
test configuration (stored procedures, UDFs, triggers, state import/export):
builder.ConfigureTestServices(services =>
{
services.UseInMemoryCosmosContainers(options =>
{
options.AddContainer("orders", "/customerId");
options.OnContainerCreated = setup =>
{
setup.ImportStateFromFile("TestData/orders-seed.json");
};
});
});For typed client patterns, seed through the client after it's created:
builder.ConfigureTestServices(services =>
{
services.UseInMemoryCosmosDB<EmployeeCosmosClient>(options =>
{
options.AddContainer("employees", "/departmentId");
options.OnClientCreated = client =>
{
var container = client.GetContainer("in-memory-db", "employees");
container.CreateItemAsync(
new { id = "emp-1", departmentId = "eng", name = "Alice" },
new PartitionKey("eng")).Wait();
};
});
});When your app uses multiple containers, seed each one in the OnHandlerCreated callback —
it fires once per container:
services.UseInMemoryCosmosDB(options =>
{
options.AddContainer("orders", "/customerId");
options.AddContainer("customers", "/id");
options.AddContainer("products", "/categoryId");
options.OnHandlerCreated = (containerName, handler) =>
{
var backing = handler.BackingContainer;
switch (containerName)
{
case "orders":
backing.CreateItemAsync(
new { id = "order-1", customerId = "cust-1", total = 42.00 },
new PartitionKey("cust-1")).Wait();
break;
case "customers":
backing.CreateItemAsync(
new { id = "cust-1", name = "Alice", tier = "gold" },
new PartitionKey("cust-1")).Wait();
break;
case "products":
backing.CreateItemAsync(
new { id = "prod-1", categoryId = "widgets", name = "Deluxe Widget" },
new PartitionKey("widgets")).Wait();
break;
}
};
});Seed through the standard Container SDK methods for highest fidelity — your seed data goes
through the same pipeline as production code.
Resolve the Container from Dependency Injection after building the host, and seed before making requests:
public class OrderApiTests : IClassFixture<OrderApiFactory>
{
private readonly OrderApiFactory _factory;
private readonly HttpClient _client;
public OrderApiTests(OrderApiFactory factory)
{
_factory = factory;
_client = factory.CreateClient();
}
[Fact]
public async Task GetOrder_ReturnsSeededOrder()
{
// Seed via SDK
var container = _factory.Services.GetRequiredService<Container>();
await container.CreateItemAsync(
new Order { Id = "order-1", CustomerId = "cust-1", Product = "Widget" },
new PartitionKey("cust-1"));
// Act
var response = await _client.GetAsync("/api/orders/order-1?pk=cust-1");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var order = await response.Content.ReadFromJsonAsync<Order>();
order!.Product.Should().Be("Widget");
}
}For tests without Dependency Injection, seed via InMemoryCosmos.Create():
public class OrderRepositoryTests : IDisposable
{
private readonly InMemoryCosmosResult _cosmos = InMemoryCosmos.Create("orders", "/customerId");
private Container Container => _cosmos.Container;
public void Dispose() => _cosmos.Dispose();
[Fact]
public async Task GetOrder_ReturnsItem()
{
// Seed
await Container.CreateItemAsync(
new Order { Id = "order-1", CustomerId = "cust-1", Product = "Widget" },
new PartitionKey("cust-1"));
// Act
var repo = new OrderRepository(Container);
var order = await repo.GetOrder("order-1", "cust-1");
// Assert
order.Product.Should().Be("Widget");
}
}Extract a reusable SeedAsync method to keep tests clean:
public class OrderTests
{
private readonly InMemoryCosmosResult _cosmos = InMemoryCosmos.Create("orders", "/customerId");
private Container Container => _cosmos.Container;
private async Task SeedAsync(params Order[] orders)
{
foreach (var order in orders)
await Container.CreateItemAsync(order, new PartitionKey(order.CustomerId));
}
[Fact]
public async Task Query_ReturnsMatchingOrders()
{
await SeedAsync(
new Order { Id = "1", CustomerId = "cust-1", Product = "Widget", Total = 10 },
new Order { Id = "2", CustomerId = "cust-1", Product = "Gadget", Total = 25 },
new Order { Id = "3", CustomerId = "cust-2", Product = "Widget", Total = 15 });
var results = await Container.GetItemQueryIterator<Order>(
new QueryDefinition("SELECT * FROM c WHERE c.customerId = @pk")
.WithParameter("@pk", "cust-1"))
.ReadNextAsync();
results.Should().HaveCount(2);
}
}Use UpsertItemAsync when you need idempotent seeding — safe to run multiple times without
conflict errors:
private async Task SeedAsync(Container container, IEnumerable<Order> orders)
{
foreach (var order in orders)
await container.UpsertItemAsync(order, new PartitionKey(order.CustomerId));
}For large datasets, seed in parallel with Task.WhenAll:
private async Task SeedManyAsync(Container container, int count)
{
var tasks = Enumerable.Range(1, count).Select(i =>
{
var order = new Order
{
Id = $"order-{i}",
CustomerId = $"cust-{i % 20}",
Product = $"Product-{i}",
Total = i * 1.5m
};
return container.UpsertItemAsync(order, new PartitionKey(order.CustomerId));
});
await Task.WhenAll(tasks);
}Use state persistence to restore a pre-built dataset. This is ideal for large, stable seed datasets that don't change between tests, or for sharing data between repositories.
Build the dataset once and export it to a JSON file:
using var cosmos = InMemoryCosmos.Create("orders", "/customerId");
// Build your data
await cosmos.Container.CreateItemAsync(new { id = "1", customerId = "c1", product = "Widget" }, new PartitionKey("c1"));
await cosmos.Container.CreateItemAsync(new { id = "2", customerId = "c2", product = "Gadget" }, new PartitionKey("c2"));
// ... more items
// Export to file
cosmos.ExportStateToFile("TestData/orders-seed.json");Restore from a file during setup:
// Direct instantiation
using var cosmos = InMemoryCosmos.Create("orders", "/customerId");
cosmos.ImportStateFromFile("TestData/orders-seed.json");
// Via Dependency Injection callback
services.UseInMemoryCosmosContainers(options =>
{
options.AddContainer("orders", "/customerId");
options.OnContainerCreated = container =>
{
container.ImportStateFromFile("TestData/orders-seed.json");
};
});
// Via OnHandlerCreated
services.UseInMemoryCosmosDB(options =>
{
options.AddContainer("orders", "/customerId");
options.OnHandlerCreated = (name, handler) =>
{
((IContainerTestSetup)handler.BackingContainer).ImportStateFromFile("TestData/orders-seed.json");
};
});If the seed data is embedded or generated programmatically:
var json = """
{
"items": [
{ "id": "1", "customerId": "c1", "product": "Widget" },
{ "id": "2", "customerId": "c2", "product": "Gadget" }
]
}
""";
container.ImportState(json);Check seed files into source control so they're available everywhere:
tests/
TestData/
orders-seed.json
customers-seed.json
products-seed.json
MyApp.Tests/
OrderTests.cs
Set the files as Content / Copy if newer in your .csproj:
<ItemGroup>
<Content Include="TestData\*.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>When you need to seed after the host is built but before tests run, capture a reference to the
IContainerTestSetup or FakeCosmosHandler and seed directly.
public class OrderApiFactory : WebApplicationFactory<Program>
{
public FakeCosmosHandler? OrdersHandler { get; private set; }
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services =>
{
services.UseInMemoryCosmosDB(options =>
{
options.AddContainer("orders", "/customerId");
options.OnHandlerCreated = (name, handler) =>
{
if (name == "orders")
OrdersHandler = handler;
};
});
});
}
}
public class OrderApiTests : IClassFixture<OrderApiFactory>
{
private readonly OrderApiFactory _factory;
public OrderApiTests(OrderApiFactory factory) => _factory = factory;
[Fact]
public async Task GetOrder_WithSeededData()
{
// Seed directly into the backing container
var backing = _factory.OrdersHandler!.BackingContainer;
await backing.CreateItemAsync(
new { id = "order-1", customerId = "cust-1", total = 99.99 },
new PartitionKey("cust-1"));
var client = _factory.CreateClient();
var response = await client.GetAsync("/api/orders/order-1");
response.StatusCode.Should().Be(HttpStatusCode.OK);
}
}public class OrderApiFactory : WebApplicationFactory<Program>
{
public IContainerTestSetup? OrdersSetup { get; private set; }
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services =>
{
services.UseInMemoryCosmosContainers(options =>
{
options.AddContainer("orders", "/customerId");
options.OnContainerCreated = setup =>
{
OrdersSetup = setup;
};
});
});
}
}When using a custom factory interface, expose state methods for seeding:
public class CosmosDbFixture : IDatabaseClientFactory, IDisposable
{
private readonly InMemoryCosmosResult _cosmos;
public CosmosDbFixture()
{
_cosmos = InMemoryCosmos.Builder()
.AddContainer("orders", "/customerId")
.AddContainer("customers", "/id")
.Build();
}
public Container GetContainer(string containerName)
=> _cosmos.Containers[containerName];
/// <summary>Direct access for registering sprocs, UDFs, triggers.</summary>
public IContainerTestSetup SetupContainer(string containerName)
=> _cosmos.SetupContainer(containerName);
/// <summary>Import state from a file for seeding.</summary>
public void ImportStateFromFile(string filePath, string containerName)
=> _cosmos.ImportStateFromFile(filePath, containerName);
public void Dispose() => _cosmos.Dispose();
}
// Seed in tests:
fixture.ImportStateFromFile("TestData/orders-seed.json", "orders");
// Or seed via the SDK Container:
await fixture.GetContainer("orders").CreateItemAsync(
new Order { Id = "1", CustomerId = "cust-1" },
new PartitionKey("cust-1"));The emulator can automatically save and restore container state between test runs. This is useful
when you want data created during one test run to be available in the next, without manually calling
ExportState/ImportState.
For the full guide — including Dependency Injection setup, direct instantiation, file naming conventions, and behaviour details — see State Persistence.
The container is shared across tests within the same factory or fixture. To avoid test pollution, clear data between tests.
Removes all items, ETags, timestamps, and change feed entries:
public class OrderTests : IAsyncLifetime, IAsyncDisposable
{
private readonly InMemoryCosmosResult _cosmos =
InMemoryCosmos.Create("orders", "/customerId");
public Task InitializeAsync() => Task.CompletedTask;
public Task DisposeAsync()
{
_cosmos.ClearItems();
return _cosmos.DisposeAsync().AsTask();
}
}public class OrderApiTests : IClassFixture<OrderApiFactory>, IAsyncLifetime
{
private readonly OrderApiFactory _factory;
public OrderApiTests(OrderApiFactory factory) => _factory = factory;
public ValueTask InitializeAsync() => ValueTask.CompletedTask;
public ValueTask DisposeAsync()
{
_factory.OrdersSetup?.ClearItems();
return ValueTask.CompletedTask;
}
}If test isolation matters more than speed, create a fresh setup for each test:
[Fact]
public async Task EachTest_GetsCleanState()
{
using var cosmos = InMemoryCosmos.Create("orders", "/customerId");
await cosmos.Container.CreateItemAsync(
new { id = "1", customerId = "c1" },
new PartitionKey("c1"));
// cosmos.Container.ItemCount... verify via query
}| Scenario | Recommended approach |
|---|---|
Integration tests with WebApplicationFactory
|
Dependency Injection callbacks for shared data, SDK methods for per-test data |
Unit tests with InMemoryCosmos
|
SDK methods with a SeedAsync helper |
| Large, stable reference datasets |
State persistence with ImportStateFromFile
|
| Data that evolves across test runs |
Automatic persistence with StatePersistenceDirectory
|
| Performance / load tests | Parallel bulk seeding or state persistence |
| Custom factory interface (Pattern 5/6) |
Direct container access via exposed ImportStateFromFile() / SetupContainer()
|
| Test isolation required | New setup per test or ClearItems() in teardown |
| Seeding across multiple containers | Multiple container callbacks |
- Setup Guide — full setup guide for all five patterns
- Unit Testing — direct container testing patterns
- Choosing Your Approach — choosing between the three integration layers
- API Reference — full API surface documentation
- Features — state persistence, change feed, TTL, and more
Getting Started
Integration & Dependency Injection
Data Management
Reference
Help