Search code examples
mongodbjunitnosqljboss-arquillianmicroprofile

Not able to access in-memory mongodb using NoSqlUnit


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.


Solution

  • 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.