2

I have a very simple Quarkus application which accepts input and insert it into MongoDB using MongoClient.

Controller:

@ApplicationScoped
@Path("/endpoint")
public class A {
    @Inject 
    B service;

    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Document add(List<? extends Document> list) {
        return service.add(list);
    }
}

Service Class:

@ApplicationScoped
public class B {

    @Inject 
    MongoClient mongoClient;

    private MongoCollection<Document> getCollection() {
        return mongoClient.getDatabase(DBname).getCollection(coll);
    }

    public Document add(List<? extends Document> list) {
        Document response = new Document();
        getCollection().deleteMany(new BasicDBObject());
        getCollection().insertMany(list);
        response.append("count", list.size());
        return response;
    }
}

As you see that my service removes existing data and inserts the new data. For JUnit testing, I am trying to set up embedded MongoDB and want my service call to use the embedded mongo. But no success.

My JUnit class

I tried out many approaches discussed on the internet to set up the embedded mongo but none worked for me.

I want to invoke my POST service but actual mongodb must not get connected. My JUnit class is as below:

@QuarkusTest
public class test {
    List<Document> request = new ArrayList<Document>(); 
    Document doc = new Document();
    doc.append("Id", "007")
       .append("name", "Nitin");
    request.add(doc);
    
    given()
         .body(request)
         .header("Content-Type", MediaType.APPLICATION_JSON)
         .when()
         .post("/endpoint")
         .then()
         .statusCode(200);
}
samabcde
  • 6,988
  • 2
  • 25
  • 41
Mowgli
  • 73
  • 2
  • 10

3 Answers3

2

You need to use a different connection-string for your test than for your regular (production) run.

Quakus can use profiles to do this, the %test profile is automatically selected when running @QuarkusTest tests.

So you can add in your application.properties something like this :

quarkus.mongodb.connection-string=mongodb://host:port
%test.quarkus.mongodb.connection-string=mongodb://localhost:27017

Here mongodb://host:port will be use on the normal run of your application and mongodb://localhost:27017 will be used from inside your test.

Then you can use flapdoodle or Testcontainers to launch a MongoDB database on localhost during your test.

More information on configuration profiles: https://quarkus.io/guides/config#configuration-profiles

More information on how to start an external service from a Quarkus test: https://quarkus.io/guides/getting-started-testing#quarkus-test-resource

loicmathieu
  • 5,181
  • 26
  • 31
  • Tried, but after adding @quarkustestresource, junit is not detecting any test class. I am completely new in junit and quarkus, simply copying pasting examples. I dont think its gonna work so easily. – Mowgli Aug 12 '20 at 11:15
  • You need to have both annotations. `@QuarkusTest` and `@QuarkusTestResource`. If you try to use Tescontainers there is a sample inside the documentation https://github.com/quarkusio/quarkus-quickstarts/blob/master/kafka-quickstart/src/test/java/org/acme/kafka/KafkaResource.java – loicmathieu Aug 12 '20 at 14:30
  • QuarkusTest QuarkusTestResource(StartMongoTest.class) public class TestClass{ Test public void testResponseStatus() { // calling my post endpoint}} – Mowgli Aug 12 '20 at 18:26
  • My StartMongoTest class is as follow. public class StartMongoTest implements QuarkusTestResourceLifecycleManager { private static MongodExecutable mongodExe; private static MongodProcess mongod; private static MongoClient mongo; public IMongodConfig getConfig(){ IMongodConfig mongodConfig = null; try{ mongodConfig = new MongodConfigBuilder() .version(Version.Main.DEVELOPMENT) .net(new Net("localhost", 27019, Network.localhostIsIPv6())) .build();} catch (Exception e) {System.out.println(e.fillInStackTrace());} return mongodConfig; } – Mowgli Aug 12 '20 at 18:28
  • Continue of mongostarttest : Override public Map start() { try { MongodStarter starter = MongodStarter.getDefaultInstance(); IMongodConfig mongodConfig = getConfig(); mongodExe = starter.prepare(mongodConfig); mongod = mongodExe.start(); System.out.println("creating mongodb"); mongo = new MongoClient() {} catch (Exception e) { System.out.println("Exception occured "+e.fillInStackTrace()); } HashMap config = new HashMap<>(); return config; } @Override public void stop() {}} – Mowgli Aug 12 '20 at 18:29
  • Console shows error : Exception occured de.flapdoodle.embed.process.exceptions.DistributionException: prepare executable for line of code mongodExe = starter.prepare(mongodConfig); – Mowgli Aug 12 '20 at 18:32
  • You can find how we start a MongoDB database with flapdoode from the test resource of our integration test: https://github.com/quarkusio/quarkus/blob/master/integration-tests/mongodb-client/src/test/java/io/quarkus/it/mongodb/MongoTestResource.java – loicmathieu Aug 13 '20 at 07:36
1

Have u tried flapdoodle:

package com.example.mongo;

import com.mongodb.BasicDBObject;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import de.flapdoodle.embed.mongo.MongodExecutable;
import de.flapdoodle.embed.mongo.MongodProcess;
import de.flapdoodle.embed.mongo.MongodStarter;
import de.flapdoodle.embed.mongo.config.IMongodConfig;
import de.flapdoodle.embed.mongo.config.MongodConfigBuilder;
import de.flapdoodle.embed.mongo.config.Net;
import de.flapdoodle.embed.mongo.distribution.Version;
import de.flapdoodle.embed.process.runtime.Network;
import java.util.Date;
import org.junit.After;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;

public class EmbeddedMongoTest
{
    private static final String DATABASE_NAME = "embedded";

    private MongodExecutable mongodExe;
    private MongodProcess mongod;
    private MongoClient mongo;

    @Before
    public void beforeEach() throws Exception {
        MongodStarter starter = MongodStarter.getDefaultInstance();
        String bindIp = "localhost";
        int port = 12345;
        IMongodConfig mongodConfig = new MongodConfigBuilder()
        .version(Version.Main.PRODUCTION)
        .net(new Net(bindIp, port, Network.localhostIsIPv6()))
        .build();
        this.mongodExe = starter.prepare(mongodConfig);
        this.mongod = mongodExe.start();
        this.mongo = new MongoClient(bindIp, port);
    }

    @After
    public void afterEach() throws Exception {
        if (this.mongod != null) {
            this.mongod.stop();
            this.mongodExe.stop();
        }
    }

    @Test
    public void shouldCreateNewObjectInEmbeddedMongoDb() {
        // given
        MongoDatabase db = mongo.getDatabase(DATABASE_NAME);
        db.createCollection("testCollection");
        MongoCollection<BasicDBObject> col = db.getCollection("testCollection", BasicDBObject.class);

        // when
        col.insertOne(new BasicDBObject("testDoc", new Date()));

        // then
        assertEquals(1L, col.countDocuments());
    }

}

Reference : Embedded MongoDB when running integration tests

suraj shukla
  • 106
  • 1
  • 6
  • I tried this. It doesnt match my requirement that I have shared in the post. I need to call my POST service from test class. All I need is that when my service is called from test, it insert data into embed db instead of actual mongodb. In your solution, you are inserting data into embed db collections, thats not my test coverage. – Mowgli Aug 11 '20 at 23:49
  • I'm sorry but i dont think that the problem is the embedded mongo here. If u r running unit test u can simply mock the mongoclient there are various ways to do so , it will simply ensure that the data does not get inserted in any actual mongo instance. On the other hand if u want it inserted into embedded mongo or a different DB ensure that reference is passed in the test case instead of the original mongo client . How to create a mongoclient for embedded mongo is also clear from the above post. Please let me know if it solves this or if im not able to understand the problem here. – suraj shukla Aug 12 '20 at 05:49
  • Quarkus works in a little different way. I used the above example but it was not getting called before executing test class. I somehow, managed to call and was getting below error : – Mowgli Aug 12 '20 at 09:18
  • If you want to (system/integration) test your REST layer, you need to use RestAssured and start a MongoDb instance with TestContainers. – Serkan Aug 12 '20 at 09:30
  • some research shows that quarkus do not support flapdoodle. is it true ?? https://github.com/quarkusio/quarkus/issues/7040 – Mowgli Aug 12 '20 at 22:16
  • @Nitin Flapdoodle didn't support AArch64 that whats this issue is about – loicmathieu Aug 13 '20 at 07:37
0

Thanks everyone for suggestions. I declared test collections in application.properties file. %test profile automatically get activated when we run junits, so automatically my services picked up the test collections. I deleted the test collections after my junit test cases got completed.

Mowgli
  • 73
  • 2
  • 10