0

I have an arquillian component test where I am wanting to use an in-memory MongoDB (Fongo) database using NoSqlUnit. I am using a @Producer to define my DataStoreConnection and I am using Eclipse MicroProfile on Java SE 8.

The issue is that after initiating the in-memory DB, I am not able to access it programmatically in my code when doing my endpoint tests.

I have a DataStoreConnectionProducer as such:

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;


@ApplicationScoped
public class DataStoreConnectionProducer {
    private MongoClient mongoClient;

    private static final Config config = ConfigProvider.getConfig();

    @Produces
    public MongoDatabase createMongoClient(){
        final String DB_PATH    = config.getValue( "mongodb.path", String.class );
        final int DB_PORT       = config.getValue( "mongodb.port", Integer.class );
        final String DB_NAME    = config.getValue( "mongodb.dbname", String.class );

        if( DB_NAME.equals( "test" ) )
            return new MongoClient().getDatabase(DB_NAME);
        else
            return new MongoClient( DB_PATH, DB_PORT ).getDatabase( DB_NAME );

    }
}

My GreetingDAO is injecting the MongoDatabase using

@Inject MongoDatabase mongoDatabase;

My Resource looks as such:

@Path( "/greetings" )
public class HelloResource {

    @Inject
    private GreetingDAO greetingDAO;

    @Inject
    private GreetingService greetingService;

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getGreeting (){
        return Response.ok(greetingDAO.findAll(), MediaType.APPLICATION_JSON).build();
    }

    @GET
    @Path( "{id}" )
    @Produces( MediaType.APPLICATION_JSON )
    public Response getGreetingById( @PathParam( "id" ) String greetingId ){
        try {
            return Response.ok( greetingDAO.findByID( greetingId.toLowerCase() ), MediaType.APPLICATION_JSON ).build();
        }catch ( NoSuchElementException ex ){
            ex.printStackTrace();
            return Response.status( 404 ).build();
        }
    }

Finally my Arquillian test:

    @RunWith( Arquillian.class )
    @RunAsClient
    public class HelloResourceTest extends AbstractTest{

        private static final String DB_NAME     = "test";

        @ClassRule
        public static final InMemoryMongoDb inMemoryMongoDb =
                newInMemoryMongoDbRule().targetPath( "localhost" ).build();
        @Rule
        public MongoDbRule embeddedMongoDbRule = newMongoDbRule()
                .defaultEmbeddedMongoDb(DB_NAME);

        @Inject MongoClient mongoClient;

        @Deployment
        public static WebArchive createDeployment () {
            WebArchive war = createBasicDeployment()
                    .addClasses(
                        HelloResource.class,
                        GreetingDAO.class,
                        GreetingService.class,
                        Greeting.class,
                        DAO.class,
                        DataStoreConnectionProducer.class
                    );
            System.out.println( war.toString(true) );
            return war;
        }

        private MongoDatabase getFongoDataBase(){
            return mongoClient.getDatabase( DB_NAME );
        }

This is pretty much where I start getting confused.. Knowing that Fongo is a in-memory DB, surely there is no remote way to access it? Rather, I would surely have to supply that to my DataStoreConnectionProducer or somehow inject it to my GreetingDAO so that the FongoDB is used rather than the @Producer trying to connect to my managed MongoDB.

A question you might ask: Why not use a Managed MongoDB? Answer: Because I wish to do component based tests, rather than Integration testing.

Hassan Nazar
  • 69
  • 10
  • Yes, you are right that Fongo does not offer any way to access it using network but only offers to access using an access directly to the created memory Java instance. – lordofthejars Aug 07 '18 at 07:21
  • 1
    You could try to create on your src/test/java a class called FongoDataStoreConnectionProducer which creates the Fongo instance. But then you would not be able to use NoSQLUnit since both instanes would be different. Another option might be to create a static factory and use embedded container so they can share same JVM, but maybe the real solution could be tackle the problem in another way, create tests using NoSQLUnit and only use the GreetingDAO class (outside CDI, Arquillian, ...) and then create another test where you use Arquillian Cube to start a real MongoDB server. – lordofthejars Aug 07 '18 at 07:25
  • I have made some "progress", having understood that @RunAsClient actually abstracts away all the underlying deployment, I would not have been able to set any values to my DataStoreConnectionProducer. But now when I'm trying to do `@Inject MongoClient fongoClient` I get `Unsatisfied depenendencies for type MongoClient.` I guess that the container CDI injection is colliding with the @Inject of our In-memory DB. Is there a way in NoSqlUnit to use Fongo in Arquillian? Ill update my question with code changes – Hassan Nazar Aug 07 '18 at 07:29
  • @lordofthejars I see, It would be neat if you could do a full component test from top(resource) to bottom(persistance) whilst getting the flexibility and speed of an in-memory DB like fongo. But I will try and take another approach to this. Thanks! – Hassan Nazar Aug 07 '18 at 07:34
  • 1
    Well basically the problem is that with you are deploying something inside the container. Another thing you can do is to not run RunAsClient, so run inside container and bundle NoSQLUnit + Fongo inside the result war. If you provide me a github project I can try to do it. – lordofthejars Aug 07 '18 at 11:18
  • Would be very appreciated: https://github.com/HasseNasse/JavaMicroServiceArquillianCourse/ under task2/start – Hassan Nazar Aug 07 '18 at 11:42
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/177568/discussion-between-hassan-nazar-and-lordofthejars). – Hassan Nazar Aug 07 '18 at 11:51

1 Answers1

0

OK after some guiding from @lordofthejars I managed to use NoSQLUnit with an In-memory MongoDB (Fongo) for my Resource Test.

I had to make a couple of changes however:

DataStoreConnectionProducer It did what it should have, but was not very testable in its implementation, I made it abit more verbose:

@Singleton
public class DataStoreConnectionFactory {
    private MongoDatabase mongoDatabase;


    @PostConstruct
    public void init(){
        this.mongoDatabase = establishConnection();
    }

    protected MongoDatabase establishConnection(){
        final Config config = ConfigProvider.getConfig();
        final String DB_PATH    = config.getValue( "mongodb.path", String.class );
        final int DB_PORT       = config.getValue( "mongodb.port", Integer.class );
        final String DB_NAME    = config.getValue( "mongodb.dbname", String.class );

        return new MongoClient( DB_PATH, DB_PORT ).getDatabase( DB_NAME );
    }

    @Produces
    public MongoDatabase getMongoDatabase(){
        return this.mongoDatabase;
    }

}

I also created a Mocked version of this Producer/Factory and added it to my test path, it looks as follows:

@Singleton
@Alternative
public class FakeDataStoreConnectionFactory {
    private MongoDatabase mongoDatabase;


    @PostConstruct
    public void init(){
        this.mongoDatabase = establishConnection();
    }

    protected MongoDatabase establishConnection(){
        return Mockito.mock(MongoDatabase.class);
    }

    @Produces
    public MongoDatabase getMongoDatabase(){
        return this.mongoDatabase;
    }

    public void setMongoDatabase(MongoDatabase db){
        this.mongoDatabase = db;
    }

}

We then defined a test-beans.xml as such:

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://xmlns.jcp.org/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
                      http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
        bean-discovery-mode="all">

    <alternatives>
        <class>path.to.my.test.folder.containing.FakeDataStoreConnectionFactory</class>
    </alternatives>
</beans>

The injection on GreetingDAO is done as it was before. However, the test has changed alittle, we have now:

  1. Removed the @RunAsClient
  2. We include the NoSQLUnit + Fongo jars in our @Deployment
  3. We added the above test-beans.xml, to define the that the alternative bean is to be initiated.
  4. We @Inject-ed the FakeDataStoreConnectionFactory and used the setter to set our mongoDatabase to the In-Memory defined one, snippets of that is shown below:

    @RunWith( Arquillian.class ) public class HelloResourceTest extends AbstractTest{

    @ClassRule
    public static final InMemoryMongoDb IN_MEMORY_MONGO_DB = newInMemoryMongoDbRule().build();
    
    @Rule
    public MongoDbRule mongoDbRule = newMongoDbRule().defaultEmbeddedMongoDb("test");
    

    @Inject FakeDataStoreConnectionFactory fakeDataStoreConnectionFactory;

    @Before
    public void setupTest(){
        fakeDataStoreConnectionFactory.setMongoDatabase( mongoDbRule.getDatabaseOperation().connectionManager().getDatabase( "test" ) );
    
    }
    

    }

The only issue I am facing now is that the @UsingDataSet annotation refuses to find my .json files, they are located under /path/to/my/test/file/initData.json. It still does not work no matter where i put them.

Hassan Nazar
  • 69
  • 10