Skip to content

MongoDB

Real MongoDB in a container or wired to existing infra. Document save/get/query, aggregation, transactions via raw client() escape hatch.

Open in setup wizard

MongoDB — wizard-synced snippet

Gradle

testImplementation("com.trendyol:stove-mongodb")

Stove configuration

Stove().with {
    mongodb {
      MongodbSystemOptions(
        configureExposedConfiguration = { cfg ->
          listOf("spring.data.mongodb.uri=${cfg.connectionString}")
        }
      )
    }
}

Test DSL

stove {
    mongodb {
      shouldGet<MyDoc>(objectId = "doc-id", collection = "orders") {
        it.status shouldBe "CREATED"
      }
    }
}

In 30 seconds Register mongodb { MongodbSystemOptions(...) }. Default collection lives under databaseOptions = DatabaseOptions(default = DefaultDatabase(name, collection)). Test DSL: save(...), shouldGet<T>(id), shouldQuery<T>(jsonFilter), shouldDelete(). Drop into client() for aggregation pipelines or transactions.

Configure

Stove().with {
  mongodb {
    MongodbSystemOptions(
      databaseOptions = DatabaseOptions(
        default = DefaultDatabase(name = "testdb", collection = "orders")
      ),
      configureExposedConfiguration = { cfg ->
        listOf("spring.data.mongodb.uri=${cfg.connectionString}")
      }
    )
  }
}.run()
Field Use
databaseOptions Default DB + collection picked when DSL omits them
configureClient Customize the MongoClient (codecs, pool size)
serde Align with your app's mapper
container = MongoContainerOptions(...) Tag, registry, raw containerFn overrides
configureExposedConfiguration Hand connectionString, host, port to AUT

DSL

Save / fetch

stove {
  mongodb {
    save(
      objectId = ObjectId.get().toHexString(),
      instance = Order(id = "1", status = "CREATED"),
      collection = "orders"   // omit to use default
    )

    shouldGet<Order>(objectId = orderHex, collection = "orders") {
      it.status shouldBe "CREATED"
    }
  }
}

Query (JSON filter)

stove {
  mongodb {
    shouldQuery<Order>(
      query = """{ "status": "CREATED", "amount": { "${'$'}gte": 100 } }""",
      collection = "orders"
    ) { orders ->
      orders shouldHaveSize 2
      orders.all { it.amount >= 100 } shouldBe true
    }
  }
}

Delete + not-exist guard

stove {
  mongodb {
    shouldDelete(objectId = orderHex, collection = "orders")
    shouldNotExist(objectId = orderHex, collection = "orders")
  }
}

Aggregation / transactions (raw client)

stove {
  mongodb {
    val totals = client().getDatabase("testdb")
      .getCollection("orders")
      .aggregate(listOf(
        Aggregates.match(Filters.eq("status", "CREATED")),
        Aggregates.group("\$customerId", Accumulators.sum("total", "\$amount"))
      ))
      .toList()

    totals shouldHaveSize 1
  }
}

Migrations

class CreateOrdersIndex : DatabaseMigration<MongodbMigrationContext> {
  override val order = 1
  override suspend fun execute(ctx: MongodbMigrationContext) {
    ctx.client.getDatabase("testdb")
      .getCollection("orders")
      .createIndex(Indexes.ascending("customerId"))
  }
}

mongodb {
  MongodbSystemOptions(/* ... */).migrations {
    register<CreateOrdersIndex>()
  }
}

Complete example

test("place order, verify Mongo doc + Kafka event") {
  stove {
    val orderId = ObjectId.get().toHexString()

    http {
      postAndExpectBody<OrderResponse>(
        "/orders",
        CreateOrderRequest(id = orderId, amount = 99.99).some()
      ) { it.status shouldBe 201 }
    }

    mongodb {
      shouldGet<Order>(objectId = orderId, collection = "orders") {
        it.status shouldBe "CREATED"
        it.amount shouldBe 99.99
      }
    }

    kafka {
      shouldBePublished<OrderCreatedEvent> {
        actual.id == orderId
      }
    }
  }
}

Pitfalls

Symptom Fix
Cannot find collection X Your code uses a different collection name; mirror exactly
ObjectId mismatch Use ObjectId.get().toHexString(); pass the hex everywhere
$ interpreted by Kotlin Escape inside strings: "${'$'}gte"

Pairs well with

  • Provided Instances for shared CI clusters (prefix collection names)
  • Bridge to verify via the app's own repository when DSL coverage gaps