Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

MongoDB

The MongoDB module provides a pre-configured container for integration testing with MongoDB.

Quick Start

open Lwt.Syntax
open Testcontainers_mongo

let test_mongo () =
  Mongo_container.with_mongo (fun container connection_string ->
    Printf.printf "MongoDB running at: %s\n" connection_string;
    Lwt.return_unit
  )

Installation

opam install testcontainers-mongo

In your dune file:

(libraries testcontainers-mongo)

Configuration

Without Authentication (Default)

(* Simple setup - no auth required *)
Mongo_container.with_mongo (fun container conn_str ->
  (* conn_str: mongodb://127.0.0.1:27017 *)
  ...
)

With Authentication

Mongo_container.with_mongo
  ~config:(fun c -> c
    |> Mongo_container.with_username "admin"
    |> Mongo_container.with_password "secret123")
  (fun container conn_str ->
    (* conn_str: mongodb://admin:secret123@127.0.0.1:27017 *)
    ...
  )

Configuration Options

FunctionDefaultDescription
with_imagemongo:7Docker image
with_usernameNoneAdmin username (optional)
with_passwordNoneAdmin password (optional)

Custom Image

Mongo_container.with_mongo
  ~config:(fun c -> c
    |> Mongo_container.with_image "mongo:6")
  (fun container conn_str -> ...)

Connection Details

Connection String

Without auth:

mongodb://127.0.0.1:27017

With auth:

mongodb://username:password@127.0.0.1:27017

Individual Components

Mongo_container.with_mongo (fun container conn_str ->
  let* host = Mongo_container.host container in
  let* port = Mongo_container.port config container in
  let username = Mongo_container.username config in
  let password = Mongo_container.password config in
  ...
)

Manual Lifecycle

let run_tests () =
  let config =
    Mongo_container.create ()
    |> Mongo_container.with_username "admin"
    |> Mongo_container.with_password "secret"
  in

  let* container = Mongo_container.start config in
  let* conn_str = Mongo_container.connection_string config container in

  (* Run tests... *)

  let* () = Testcontainers.Container.terminate container in
  Lwt.return_unit

Using with MongoDB Libraries

With mongo (OCaml MongoDB driver)

open Lwt.Syntax

let test_with_mongo_driver () =
  Mongo_container.with_mongo (fun container conn_str ->
    (* Parse connection string or use components *)
    let* host = Mongo_container.host container in
    let* port = Mongo_container.port (Mongo_container.create ()) container in

    (* Connect using your MongoDB library *)
    (* Example with a hypothetical OCaml MongoDB client:
       let* client = Mongo.connect ~host ~port () in
       let db = Mongo.database client "testdb" in
       let collection = Mongo.collection db "users" in
       ...
    *)

    Printf.printf "Connected to MongoDB at %s:%d\n" host port;
    Lwt.return_unit
  )

Data Setup Using mongosh

Create Collections and Indexes

let setup_database container =
  let* (exit_code, output) = Testcontainers.Container.exec container [
    "mongosh"; "--eval"; {|
      use testdb;

      db.createCollection("users");
      db.users.createIndex({ email: 1 }, { unique: true });

      db.createCollection("posts");
      db.posts.createIndex({ userId: 1 });
      db.posts.createIndex({ createdAt: -1 });
    |}
  ] in
  if exit_code <> 0 then
    Printf.printf "Setup failed: %s\n" output;
  Lwt.return_unit

Insert Test Data

let seed_data container =
  let* (_, _) = Testcontainers.Container.exec container [
    "mongosh"; "--eval"; {|
      use testdb;

      db.users.insertMany([
        { email: "alice@example.com", name: "Alice", age: 30 },
        { email: "bob@example.com", name: "Bob", age: 25 },
        { email: "charlie@example.com", name: "Charlie", age: 35 }
      ]);

      db.posts.insertMany([
        { userId: "alice", title: "First Post", content: "Hello!" },
        { userId: "bob", title: "Bob's Post", content: "Hi there!" }
      ]);
    |}
  ] in
  Lwt.return_unit

Verify Data

let verify_data container =
  let* (exit_code, output) = Testcontainers.Container.exec container [
    "mongosh"; "--quiet"; "--eval"; {|
      use testdb;
      JSON.stringify(db.users.find().toArray());
    |}
  ] in
  Printf.printf "Users: %s\n" output;
  Lwt.return (exit_code = 0)

Complete Test Example

open Lwt.Syntax
open Testcontainers
open Testcontainers_mongo

let with_test_mongo f =
  Mongo_container.with_mongo
    ~config:(fun c -> c
      |> Mongo_container.with_username "testadmin"
      |> Mongo_container.with_password "testpass")
    (fun container conn_str ->
      (* Setup initial data *)
      let* _ = Container.exec container [
        "mongosh"; "-u"; "testadmin"; "-p"; "testpass";
        "--authenticationDatabase"; "admin"; "--eval";
        {|
          use appdb;
          db.items.insertOne({ name: "Test Item", price: 9.99 });
        |}
      ] in
      f container conn_str
    )

let test_find_items _switch () =
  with_test_mongo (fun container _conn_str ->
    let* (exit_code, output) = Container.exec container [
      "mongosh"; "-u"; "testadmin"; "-p"; "testpass";
      "--authenticationDatabase"; "admin"; "--quiet"; "--eval";
      {| use appdb; db.items.countDocuments(); |}
    ] in
    Alcotest.(check int) "exit code" 0 exit_code;
    Alcotest.(check bool) "has output" true (String.length output > 0);
    Lwt.return_unit
  )

let () =
  Lwt_main.run (
    Alcotest_lwt.run "MongoDB Tests" [
      "items", [
        Alcotest_lwt.test_case "find items" `Slow test_find_items;
      ];
    ]
  )

Wait Strategy

The MongoDB module waits for:

Waiting for connections

This indicates MongoDB is ready to accept client connections.

Replica Sets

For testing replica set features:

let setup_replica_set () =
  let request =
    Container_request.create "mongo:7"
    |> Container_request.with_cmd ["--replSet"; "rs0"]
    |> Container_request.with_exposed_port (Port.tcp 27017)
  in

  Container.with_container request (fun container ->
    (* Initialize replica set *)
    let* _ = Container.exec container [
      "mongosh"; "--eval"; "rs.initiate()"
    ] in
    (* Wait for replica set to be ready *)
    let* () = Lwt_unix.sleep 5.0 in
    (* Now use the replica set *)
    Lwt.return_unit
  )

Troubleshooting

Authentication Failed

When using authentication:

(* Both username and password must be set *)
Mongo_container.with_username "admin"
|> Mongo_container.with_password "secret"

(* Connection string must include credentials *)
(* mongodb://admin:secret@127.0.0.1:27017 *)

Connection Refused

Ensure the container is fully started:

(* Use with_mongo which handles waiting *)
Mongo_container.with_mongo (fun container conn_str ->
  (* Safe to connect here *)
  ...
)

mongosh Not Found

Older MongoDB images use mongo instead of mongosh:

(* For MongoDB < 6.0 *)
Container.exec container ["mongo"; "--eval"; "..."]

(* For MongoDB >= 6.0 *)
Container.exec container ["mongosh"; "--eval"; "..."]

Slow Startup

MongoDB typically starts in 5-10 seconds, but can be slower:

Container_request.with_startup_timeout 60.0