Unit test your Spring/Hibernate and MongoDB setup

In my previous post I described how you can setup Spring and Hibernate in combination with the MongoDB. In this post I show you how you can write your unit test for this setup including a workaround for the fact that there isn’t an embedded version of MongoDB.

Of course you can check manually if your code works by checking the content of the MongoDB. You can use the MongoDB shell or the REST interface by using ‘curl’ (when using REST make sure you start the database with the ‘–rest’ option). For example see the result of curl 'http://localhost:28017/pascalalma/logItem/'

pascal@~$ curl ‘http://localhost:28017/pascalalma/logItem/
{
“offset” : 0,
“rows”: [
{ “_id” : { “$oid” : “4e1c2ffca0eee881985e04f0” }, “_class” : “net.pascalalma.mongo.entity.LogItem”, “message” : “just some text 0” } ,
{ “_id” : { “$oid” : “4e1c2ffda0eee881985e04f1” }, “_class” : “net.pascalalma.mongo.entity.LogItem”, “message” : “just some text 1” } ,
{ “_id” : { “$oid” : “4e1c2ffda0eee881985e04f2” }, “_class” : “net.pascalalma.mongo.entity.LogItem”, “message” : “just some text 2” } ,
{ “_id” : { “$oid” : “4e1c4289a0ee3d15a8af39d3” }, “_class” : “net.pascalalma.mongo.entity.LogItem”, “message” : “just some text 0” }
],
“total_rows” : 3 ,
“query” : {} ,
“millis” : 0
}
pascal@~$

However, when building software you want your test to be performed automatically so we use JUnit to perform the tests and checks. And I use JUnit to start and stop my local MongoDB instance. Here is how it works (I assume you have the same project setup as I created in my previous post):

  • Wire the Spring beans in your test context
  • I created a SpringContext class for testing purposes:

    package net.pascalalma.mongo;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    /**
     * Only to be used in test classes!!
     * 
     * @author pascal
     */
    public class MyTestApplicationContext {
    
        private static MyTestApplicationContext testContext = null;
    
        ApplicationContext context = null;
    
        private MyTestApplicationContext() {
        }
    
        public static MyTestApplicationContext getInstance()
        {
            if (testContext == null) {
                testContext = new MyTestApplicationContext();
                testContext.initialise();
            }
            return testContext;
        }
    
        public void initialise() {
             context = new ClassPathXmlApplicationContext(
                    new String[]{"spring-config.xml"});
        }
    
        public Object getBean(String name) {
            return context.getBean(name);
        }
    
        public ApplicationContext getSpringContext() {
            return context;
        }
    }
    

    With this class I can access my Spring beans easily in my Unit tests.

  • Write the test class
  • I use ProcessBuilder to start and stop the local MongoDB instance before the test class is executed. The base Testclass looks like this:

    package net.pascalalma.mongo;
    
    import org.apache.log4j.Logger;
    import org.junit.After;
    import org.junit.AfterClass;
    import org.junit.Before;
    import org.junit.BeforeClass;
    
    public class MongoDBTest {
    
        private static final Logger logger = Logger.getLogger(MongoDBTest.class);
        static Process p = null;
    
        @Before
        public void setUp() throws Exception {
            //@Todo Run mongo with a test specific .js file to produce initial data state
        }
    
        @After
        public void tearDown() throws Exception {
            //@Todo Drop database
        }
    
        @BeforeClass
        public static void beforeClass() throws Exception {
            String[] command = new String[]{"/Users/pascal/development/mongodb-osx-x86_64-1.8.2/bin/mongod", "--dbpath", "/Users/pascal/development/mongodb/data", "--rest"};
    
            ProcessBuilder pb = new ProcessBuilder(command);
    
            p = pb.start();
    
            logger.debug("Process started with pid: " + p);
    
        }
    
        @AfterClass
        public static void afterClass() throws Exception {
            // Stop mongod process
            boolean processClosed = false;
    
            Thread.sleep(500);
            if (p != null) {
                while (!processClosed) {
    
                    try {
                        p.destroy();
                        processClosed = true;
                        Thread.sleep(500);
                        logger.info(" Process destroyed: " + p.exitValue());
                    } catch (IllegalThreadStateException itse) {
                        logger.warn(itse);
                        processClosed = false;
                    }
                }
            }
        }
    }
    

    And the actual test class:

    package net.pascalalma.mongo;
    
    import junit.framework.Assert;
    import net.pascalalma.mongo.entity.LogItem;
    import net.pascalalma.mongo.services.LogService;
    import org.apache.log4j.Logger;
    import org.bson.types.ObjectId;
    import org.junit.Before;
    import org.junit.Test;
    
    public class LogServiceTest extends MongoDBTest {
    
        private static final Logger logger = Logger.getLogger(LogServiceTest.class);
        private LogService service = null;
    
        @Test
        public void testCreateAndFindLog() throws Exception {
    
            ObjectId id = null;
    
            for (int i = 0; i < 3; i++) {
                LogItem log = new LogItem();
                log.setMessage("just some text " + i);
    
                id = service.add(log);
                logger.debug("log.id = " + id);
            }
    
            LogItem logFound = service.get(id);
            logger.debug("log = " + logFound.toString());
            Assert.assertNotNull(logFound);
        }
    
        @Before
        public void setUp() throws Exception {
            logger.info("setting up test");
            super.setUp();
            service = (LogService) MyTestApplicationContext.getInstance().getBean("logService");
            //@TODO Run mongo with a test specific .js file to produce initial data state
        }
    }
    
  • Run it!
  • The test output shows it ran successfully:

    ——————————————————-
    T E S T S
    ——————————————————-
    Running net.pascalalma.mongo.LogServiceTest
    DEBUG [main] net.pascalalma.mongo.MongoDBTest: Process started with pid: java.lang.UNIXProcess@603b1d04
    INFO [main] net.pascalalma.mongo.LogServiceTest: setting up test
    DEBUG [main] net.pascalalma.mongo.services.MongoDBLoggerServiceImpl: Adding a new LogItem instance
    DEBUG [main] net.pascalalma.mongo.LogServiceTest: log.id = 4e2fc241a0eea3de77684fdf
    DEBUG [main] net.pascalalma.mongo.services.MongoDBLoggerServiceImpl: Adding a new LogItem instance
    DEBUG [main] net.pascalalma.mongo.LogServiceTest: log.id = 4e2fc241a0eea3de77684fe0
    DEBUG [main] net.pascalalma.mongo.services.MongoDBLoggerServiceImpl: Adding a new LogItem instance
    DEBUG [main] net.pascalalma.mongo.LogServiceTest: log.id = 4e2fc241a0eea3de77684fe1
    DEBUG [main] net.pascalalma.mongo.LogServiceTest: log = LogItem [id=4e2fc241a0eea3de77684fe1, message=just some text 2, timestamp=null]
    INFO [main] net.pascalalma.mongo.MongoDBTest: Process destroyed: 12
    Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.798 sec

    Results :

    Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

There is definitely room for improvement, like adding the possibility to run ‘.js’ scripts to set the database in a predefined state in the @Before method of the test class and remove the database in the @After method.
Another thing that bothered me is the ‘Thread.sleep’ that I had to add at some places to make it work. Nevertheless I showed how to setup a basic test case with MongoDB/Spring combination and if you have a better solution please let me know!

About Pascal Alma

Pascal is a senior IT consultant and has been working in IT since 1997. He is monitoring the latest development in new technologies (Mobile, Cloud, Big Data) closely and particularly interested in Java open source tool stacks, cloud related technologies like AWS and mobile development like building iOS apps with Swift. Specialties: Java/JEE/Spring Amazon AWS API/REST Big Data Continuous Delivery Swift/iOS
This entry was posted in MongoDB, Spring Framework and tagged , . Bookmark the permalink.

1 Response to Unit test your Spring/Hibernate and MongoDB setup

  1. Shiraz says:

    There are situations where installing the software is simply denied/unfavorable, normally happens in case of CI (Continuous integration) testing with Bamboo or Hudson. Where the machine running the server has no MongoDB installed. It is likely that all the persistence and even business tests fail badly. I am not able to find the solution until now, however, do you have any idea to unit test the application in such circumstances.

    Thanks for sharing your experiences in advance!

Comments are closed.