Start
Configuration
Every property is optional. The defaults are chosen so that adding the annotation and nothing else gives you a correct, isolated test.
Test properties
Declare these in src/test/resources/application.properties, application.yml, a test profile, or @SpringBootTest(properties = ...). They are read once, when the application context is initialised.
| Property | Default | Description |
|---|---|---|
bonsai.collectionsList<String> | — | Collections to pre-create: one property, with dot notation deciding the database. Unqualified names (orders) land in the default database; qualified names (billing.invoices) target a specific one; a reporting.* entry declares the database alone. Merged with the annotation attributes. Everything the application touches materialises lazily anyway, so declaring is never required. |
bonsai.indexes-fileString | — | Optional index declarations. MongoDB is schemaless, and so is the emulation. nothing has to be declared for collections to exist. This file exists for one thing: the indexes your production database has (unique indexes change behaviour, such as duplicate-key errors, when they are created outside the application, by ops scripts or Atlas. If your app creates its own indexes at startup, you do not need it. Resolved through Spring's Resource abstraction. |
bonsai.seed.locationsList<String> | — | Suite-level seed locations, applied to every annotated class. Accepts classpath:, classpath*:, file:, or a plain filesystem path. |
bonsai.seed.modeString | per-class | per-class reapplies suite seeds after every cleanup; per-method applies them once, when the context starts. Any other value fails fast. |
bonsai.cleanupString | drop-databases | drop-databases, clear, or none. Any other value fails fast. |
bonsai.bean-overrides.enabledboolean | true | Whether the bean-override post-processor is installed at all. Turning it off leaves your application beans exactly as declared, which means they will try to reach a real server. |
bonsai.reactive.allow-unmanagedboolean | false | Embedded mode refuses to boot when the test context defines a reactive MongoDB bean (com.mongodb.reactivestreams.client.MongoClient or ReactiveMongoDatabaseFactory), because reactive beans cannot be rewired to the emulator and would dial the real server for the whole run. The check runs on bean definitions, after they all exist and before any is instantiated; a reactive driver merely on the classpath only warns. Set true to boot anyway, accepting that reactive beans are unmanaged. |
bonsai.neutralise-propertiesList<String> | — | Additional application property names holding a MongoDB URI, rewritten to a neutral local URI so an un-seamed client fails fast instead of dialling production. See Multi-tenant applications. |
bonsai.object-mapper-bean-nameString | sharedMongoTestObjectMapper | Bean name of the ObjectMapper the harness uses. The bean is not auto-registered. Declare one under this name only if you need to override the default behaviour. |
bonsai.modeString | embedded | embedded or passthrough. See Passthrough mode. |
bonsai.passthrough.uriString | — | Real MongoDB URI; required when mode=passthrough. |
bonsai.passthrough.allow-remoteboolean | false | Explicitly allows a passthrough URI containing a non-loopback host. Use only for a dedicated disposable test deployment: per-method cleanup drops known databases on that server. |
bonsai.record.outputString | — | Enables record-and-replay and names the artifact root. Passthrough-only, Spring-only; snapshots each class's post-seed state as a ready-made fixture directory. See Record & replay. |
bonsai.record.includeString | — | FQN glob restricting which classes are recorded. ? is a literal, not a wildcard. |
bonsai.record.databasesList<String> | — | Explicit database allowlist: records a "complete": true manifest; scope is otherwise inferred and marked false. |
bonsai.record.allow-remoteboolean | false | Allows recording against a non-local host. Off by default. |
bonsai.record.max-documentsint | 50000 | Volume cap across a recording; exceeding it is a named refusal. |
bonsai.durability.directoryString | — | Debug aid. Backs the embedded store with a file in this directory instead of memory, so you can inspect it after a run. |
bonsai.experimental.change-streamsboolean | false | Experimental. Enables collection.watch() / database.watch(); off is byte-identical to 3.3.0. See Change streams. |
bonsai.experimental.change-streams.log-limitint | 10000 | Bounded change-event log retained for resume tokens; history evicted past it fails with the history-lost signature. |
List-valued properties
The list properties bind either as a YAML list or as a comma-separated string, so both shapes below are equivalent.
bonsai.collections=orders,outbox,billing.invoices,reporting.*
bonsai.seed.locations=classpath:mongo/seed/
bonsai.seed.mode=per-class
bonsai.cleanup=drop-databasesAnnotation attributes
Three attributes, on both the Spring and the plain-JUnit annotation. The list attributes are merged with the collections property rather than overriding it, so a suite-wide property and a class-specific attribute compose.
| Property | Default | Description |
|---|---|---|
databases()String[] | {} | Databases to pre-create. |
collections()String[] | {} | Collections to pre-create. orders lands in the default database; billing.invoices targets database billing; billing.* declares the database alone. |
defaultDatabase()String | "" | The database an unqualified collection name resolves to. Empty means resolve from configuration. |
@SpringBootTest
@EnableMongoEmulator(
databases = "billing",
collections = {"orders", "outbox", "billing.invoices"},
defaultDatabase = "shop")
class OrderFlowIT {
}Default database resolution
An unqualified name like "orders" has to land somewhere. The Spring harness resolves the target in this order:
- the annotation's
defaultDatabase, when set; spring.mongodb.database, when set;- the database segment of
spring.mongodb.uri, when present; test.
The generic harness drops the Spring configuration steps and falls back straight to test. Names split at the first dot, so billing.a.b means collection a.b in database billing.
Cleanup between tests
After every test method, the harness drops every database that method touched. Not the databases you declared, meaning the ones actually written to and tracked as the test runs. That is why a test never has to clean up after itself, and why test order never matters.
# Drop every database the test touched. The default, and the one to keep.
bonsai.cleanup=drop-databases
# Delete documents but keep collections and their indexes.
bonsai.cleanup=clear
# Do nothing. State bleeds between test methods; you are on your own.
bonsai.cleanup=noneSeed mode
per-class, the default, reapplies suite seeds after every cleanup, so every test method receives the same suite fixture. per-method applies suite seeds once, when the context starts; after a method consumes them and cleanup runs, later methods may see them absent. Use per-class when every method needs the fixture, and per-method when that one-time lifecycle is intentional.
Bean overrides
With overrides enabled (the default), the context initializer replaces the application's MongoClient with the embedded one, and its MongoDatabaseFactory with one built on that client, keeping the configured database name. Your MongoTemplate, GridFsTemplate, repositories, and MongoTransactionManager beans are left completely alone: they pick up the replacement through the factory, exactly as they would in production.
Applications that build clients themselves, with multi-tenant routing being the common case, need a small seam so the harness has something to replace. That is covered in Multi-tenant applications.
Properties the harness publishes
Before the context refreshes, the initializer rewrites the connection properties so that nothing in the application can reach a real server by accident:
| Property | Default | Description |
|---|---|---|
spring.mongodb.uri | mongodb://localhost:1/<database> | Read for its database segment during default-database resolution, then rewritten to a neutral local URI with no parameters. |
spring.mongodb.host | localhost | Rewritten. |
spring.mongodb.port | 27017 | Rewritten. |
spring.mongodb.database | <defaultDatabase> | Set to the resolved default database. |
bonsai.active | true | Published so application configuration can branch on the harness being present. @ConditionalOnProperty, a profile guard, or an assertion that the harness really is in play. |
Property names listed in neutralise-properties are rewritten the same way, with ?serverSelectionTimeoutMS=100 appended so a client that still dials out fails in a tenth of a second rather than hanging for thirty.
# The application reads its connection string from a custom key
bonsai.neutralise-properties=mongo.uri,tenants.default.uriOn-disk durability
By default the embedded store lives entirely in memory and vanishes with the JVM. Point bonsai.durability.directory at a directory and it is backed by a store file written there instead, which is useful when you want to open the store after a failing run and look at what was actually written. The generic harness reads the same setting from the bonsai.durability.directory system property.
The file-backed store needs one extra test-scoped dependency. io.typecheck:bonsai-durability, which delivers the Nitrite MVStore persistence adapter transitively at the version Bonsai was built against. The adapter is optional inside bonsai-core so the in-memory default pulls nothing. See Installation for the coordinates and Modes for the error you get without it.
The generic harness
Without Spring there is no Environment, so there are no bonsai.* properties. Configuration is the annotation attributes, and everything else is a method call.
@EnableMongoEmulator(
databases = "billing",
collections = {"orders", "billing.invoices"},
defaultDatabase = "shop")
class OrderStoreTest {
}