ravendb-test-driver runs integration tests against a real RavenDB server instead of a mock. It
creates an isolated database for each test and deletes that database when its DocumentStore is
closed. Your tests use the standard ravendb client API.
pip install ravendb-test-driverPython 3.10+ is required.
With the default configuration, the driver starts an embedded RavenDB server and gives every store its own database:
from ravendb_test_driver import RavenTestDriver
with RavenTestDriver() as driver:
with driver.get_document_store() as store:
with store.open_session() as session:
session.store({"name": "John"}, "people/1")
session.save_changes()
with store.open_session() as session:
assert session.load("people/1", dict)["name"] == "John"The default mode requires a matching system .NET runtime. The two alternatives below do not require .NET on the machine running the Python tests.
| Mode | .NET on the test machine? | Who manages the server? | Best for |
|---|---|---|---|
| Embedded default | Yes | Test driver | The simplest local setup |
| On-demand self-contained | No | Test driver | Portable developer machines and CI runners |
| Attach to your server | No | You | Docker, Testcontainers, or a shared service |
Configure the selected mode before the first call to get_document_store().
No configuration is needed. The driver starts the framework-dependent server bundled with
ravendb-embedded.
ravendb-test-driver version |
Required runtime |
|---|---|
| 7.2.x | .NET 10 |
| 7.1.x | .NET 8 |
Run dotnet --list-runtimes and look for Microsoft.NETCore.App. Re-check the requirement when
upgrading to a new RavenDB minor version.
Runnable walkthrough: Lab 02 — isolated embedded databases.
Embedded test servers run in memory, so the create-and-delete-a-database cycle behind every
get_document_store() call never lands on disk. Only the server log is written, to a scratch
directory the driver removes when the interpreter exits.
Two consequences worth knowing:
- A large fixture seeded in
setup_databaseis held in RAM rather than spilled to disk. - Nothing survives a server restart, and there are no files to inspect after a failing run.
To go back to disk-backed storage, either set the argument yourself, which the driver never overrides:
options = TestServerOptions()
options.command_line_args.append("--RunInMemory=false")
options.data_directory = "/path/you/choose"
RavenTestDriver.configure_server(options)or switch it off on the options themselves:
options = TestServerOptions()
options.run_in_memory = False
RavenTestDriver.configure_server(options)The driver also redirects the data directory when you leave it at the ravendb-embedded default,
which otherwise points inside the installed package. Set data_directory explicitly and the
driver leaves your path alone.
Nothing closes the shared test server before interpreter exit. Call
RavenTestDriver.stop_test_server() from a session-scoped fixture teardown when you want that
cost inside your test run rather than after the runner prints its summary; the server starts
again on the next get_document_store().
Pass a server certificate together with the client PEM the tests authenticate with, and the driver wires that client material into every store it hands out:
options = TestServerOptions()
options.secured("server.pfx", "client.pem", ca_certificate_path="ca.crt")
RavenTestDriver.configure_server(options)The client PEM is required here: a secured server the test client cannot authenticate to is rejected before the server starts.
Runnable walkthrough: Lab 05 — secured embedded server.
Let the driver download, cache, and manage the self-contained build for the current platform:
from ravendb_test_driver import RavenTestDriver, TestServerOptions
options = TestServerOptions()
options.with_auto_downloaded_server()
RavenTestDriver.configure_server(options)
with RavenTestDriver() as driver:
with driver.get_document_store() as store:
...TestServerOptions is a ravendb_embedded.ServerOptions that names the intent. configure_server
still accepts a plain ServerOptions, and the driver applies the same test defaults either way.
The same test configuration works across supported Windows, Linux, and macOS machines because the
operating system and architecture are detected at runtime. The first run downloads 100 MB+;
later runs reuse ~/.cache/ravendb-embedded. Pass cache_root to
with_auto_downloaded_server() when your build system restores a different cache directory.
Supported targets are Windows x64/x86, Linux x64/ARM64, and macOS x64/ARM64. Self-contained mode removes the system .NET requirement, but normal RavenDB operating-system dependencies still apply. Minimal Linux images may need their distribution's ICU package. The Python wheel stays platform-independent because it downloads only the self-contained build needed by the current machine rather than bundling every platform.
Runnable walkthrough: Lab 04 — portable embedded tests without .NET.
Start RavenDB yourself—locally, in Docker or Testcontainers, or as a shared service—and configure its URL:
from ravendb_test_driver import RavenTestDriver
RavenTestDriver.configure_external_server("http://localhost:8080")Alternatively, configure the URL through the environment:
RAVENDB_TEST_SERVER_URL=http://localhost:8080 python -m unittestThis path does not use EmbeddedServer: the driver neither starts nor stops the server, but it
still creates and deletes an isolated database for each test. No .NET installation is needed on
the test machine; the server environment supplies its own runtime.
For HTTPS with client-certificate authentication:
RavenTestDriver.configure_external_server(
"https://my-ravendb",
certificate_pem_path="client.pem",
trust_store_path="ca.crt",
)The equivalent environment variables are:
RAVENDB_TEST_SERVER_URLRAVENDB_TEST_SERVER_CERTRAVENDB_TEST_SERVER_CA
trust_store_path or RAVENDB_TEST_SERVER_CA is needed when the server's CA is not already
trusted by the test machine.
Explicit configuration wins over the environment. If a test calls configure_server() and
RAVENDB_TEST_SERVER_URL is also set, the environment variable is ignored and a warning is
emitted, because the driver creates and hard-deletes databases on whichever server it uses. To let
the environment pick the server, do not call configure_server().
Runnable walkthrough: Lab 01 — Docker, Testcontainers, and shared servers.
Create a RavenTestDriver for the test or fixture and register its cleanup once. Closing the driver
closes any store you left open and deletes its database, so nothing leaks if a test throws halfway
and you never have to close a store yourself:
from unittest import TestCase
from ravendb_test_driver import RavenTestDriver
class TestPeople(TestCase):
def setUp(self):
self.driver = RavenTestDriver()
self.addCleanup(self.driver.close) # the only cleanup line you need
def test_stores_a_person(self):
store = self.driver.get_document_store()
with store.open_session() as session:
session.store({"name": "John"}, "people/1")
session.save_changes()Closing stores yourself is still fine, and it is what you want when one test creates several databases and the order they go away in matters:
with RavenTestDriver() as driver:
with driver.get_document_store() as store:
...Each get_document_store() call creates a new database. Closing the store deletes it, which keeps
tests independent even when they share one RavenDB server process. Database names are generated
(test_1, test_2, ...) from a process-wide counter; treat them as opaque and read
store.database rather than assuming a name, or pass database="..." to pick the stem yourself.
If closing the driver hits errors, it raises DriverCloseError, a RuntimeError subclass whose
exceptions attribute holds every original exception rather than a joined string.
Override setup_database(self, store) to create indexes or seed reference data whenever the
driver creates a database:
class PeopleTestDriver(RavenTestDriver):
def setup_database(self, store):
with store.open_session() as session:
session.store({"name": "Seeded"}, "people/seed")
session.save_changes()Override pre_configure_database(self, database_record) to change the database itself before it is
created, for settings, revisions, expiration, encryption or topology:
class PeopleTestDriver(RavenTestDriver):
def pre_configure_database(self, database_record):
database_record.settings["Indexing.MapTimeoutInSec"] = "30"Use GetDocumentStoreOptions.wait_for_indexing_timeout when a store should not be returned until
indexing settles, or call wait_for_indexing(store) directly. It waits until every applicable
index is non-stale and any side-by-side replacement has been swapped in.
wait_for_user_to_continue_the_test(store) prints the Studio URL for that database, opens a
browser, and blocks until a document with the id Debug/Done shows up in the database. Store one
from Studio to continue; the driver deletes the marker so a later wait on the same store still
blocks.
The wait is unbounded, because you are the one looking at Studio. Pass a timeout to bound it and
get a TimeoutException instead. A CI job protects itself from a call left in committed code with
RAVENDB_TEST_WAIT_FOR_USER=0, which skips the wait entirely.
Runnable walkthrough: Lab 03 — seeding and indexes.
Defaults are chosen so an existing suite keeps working. These are the knobs worth knowing:
| Switch | Default | What it does |
|---|---|---|
TestServerOptions.run_in_memory |
True |
Runs embedded test servers in memory. Set False on the options you pass to configure_server to go back to disk |
RavenTestDriver.use_caller_name_for_database |
False |
Names databases after the calling test (test_stores_a_person_3) instead of test_3 |
RAVENDB_TEST_UNIQUE_DB_NAMES |
off | Adds the process id to database names, so parallel runners sharing one attached server stop colliding |
RAVENDB_TEST_WAIT_FOR_USER |
on | Set to 0 to skip wait_for_user_to_continue_the_test entirely |
Anything describing the server itself belongs on the options object; the environment variables exist so a CI job can flip a switch without editing test code.
Caller-name databases are sanitized to [A-Za-z0-9_.-], and fall back to test when the caller has
no usable name, such as a lambda or a module-level call.
The client sends its requests through requests, which honors HTTP_PROXY, so any interception
proxy works without driver support:
HTTP_PROXY=http://127.0.0.1:8080 python -m unittestOn Windows, proxy bypass rules skip loopback addresses, so traffic to 127.0.0.1 never reaches the
proxy. Bind the test server to the machine name instead, which also needs unsecured access to be
allowed on the private network:
import socket
from ravendb_test_driver import RavenTestDriver, TestServerOptions
options = TestServerOptions()
options.server_url = f"http://{socket.gethostname()}:0"
options.command_line_args.append("--Security.UnsecuredAccessAllowed=PrivateNetwork")
RavenTestDriver.configure_server(options)That pair is what TestServerOptions.UseFiddler() does in the .NET test driver.
| Lab | Scenario | Needs system .NET? |
|---|---|---|
| 01 | Attach to Docker, Testcontainers, or a shared server | No |
| 02 | Default embedded server and isolated databases | Yes |
| 03 | Seed data and wait for real indexing | Yes |
| 04 | On-demand self-contained server | No |
| 05 | Secured embedded server with client certificates | Yes |
The runnable scripts live in this repository rather than site-packages. Clone or download the
repository, install the package, and run them from the repository root. See the
complete labs guide.
For lower-level server configuration, see
ravendb-embedded.