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