Fork me on GitHub

Testing your LDAP code with OpenDJ

In the age of TDD and unit testing, you have to be able to test your code easily and fast.
If testing with SQL databases is well documented, this is not the case for LDAP directories. Hopefully, with the current modern libre LDAP Java servers, this feature is becoming available.
ApacheDS offers a JUnit runner, but we meet with problems trying to use it. Thus we decided to use OpenDJ which can be easily embedded and which can use an in-memory backend so performance should be acceptable for tests.
Nevertheless, even if OpenDJ uses an in-memory backend, its configuration is managed with files. That's why you will have to add those files to your test environment.

With JUnit, we have two ways to manage external resources:

  • use a specific JUnit runner that will manage the reference to the embedded server (this is the choice of ApacheDS)
  • use a JUnit rule that will start and stop the embedded server. This is our choice, using a ClassRule, a rule that is applied to a static variable of the test class. This feature is available since JUnit 4.10.

In order to simplify the configuration of the OpenDJ server we have defined an annotation, CreateLdapServer, to pass some parameters to our instance:

  • a ldif file containing the data to be inserted into the directory
  • the path (relative or absolute) to the directory containing the minimum configuration of OpenDJ
  • the in-memory backend id where the test data will be stored
  • the Distinguished Name for the in-memory backend

Now, we only need to write our rule class that implements org.junit.rules.TestRule.
So let's write the required method:

   
@Override
public Statement apply(Statement stmnt, Description d) {
  CreateLdapServer annotation = d.getAnnotation(CreateLdapServer.class);
  if (annotation != null) {
    return statement(stmnt, annotation.serverHome(), annotation.ldifConfig(), annotation.
        ldifFile(), annotation.backendID(), annotation.baseDN());
  }
  return stmnt;
}            
          

The first line is looking for our CreateLdapServer annotation, and if one is found then the execution statement for the test is wrapped so that we can start the LDAP server when the class is loaded and stop it after all the tests in the test class have run. This is what you can see in the following code:

private Statement statement(final Statement stmnt, final String serverHome,
  final String ldifConfigFile, final String ldifFile, final String backendId,
  final String dn) {
  return new Statement() {
    @Override
    public void evaluate() throws Throwable {
      before();
      try {
        stmnt.evaluate();
      } finally {
        after();
      }
    }
    private void after() {
      stopLdapServer();
    }
    private void before() {
      try {
        startLdapServer(serverHome, ldifConfigFile);
        loadLdif(ldifFile, backendId, DN.decode(dn));
      } catch (Exception ex) {
        ex.printStackTrace();
        throw new RuntimeException("Could'nt start LDAP Server", ex);
      }
    }
  };
}            
          

You can see that we create an instance of org.junit.Statement wrapping the test statement with two methods: before() called before the test execution and after() when all the test has run, whatever its result is. These two methods are quite simple:

  • before() starts the LDAP server and load the ldif file into it,
  • after() stops the LDAP server.

Since all the data are stored in an in-memory backed we don't need to clean them before stoping the server. Look into the code if you want to know exactly how we start the server and create the in-memory backend.

If you want to use all of this in your test, this is how you should proceed.
First, annotate your test code with the CreateServer annotation:

 
@CreateLdapServer(ldifConfig="opendj/config/config.ldif", serverHome="opendj", ldifFile="silverpeas-ldap.ldif")
public class LDAPDriverTest {
...
       

Then declare our rule:

 
@ClassRule
public static OpenDJRule ldapRule = new OpenDJRule();
      

Now you are almmost ready to test your code. Add the OpenDJ required librairies to your test classpth. In the case of an Apache Maven project you will have to define the following dependencies:

...
    
      org.forgerock.opendj
      opendj-server
     2.4.5
      test
    
    
      com.sleepycat
      je
      1.4.7
      test
    
...            
          

All you need now are some configuration files:

  • the ldif file (silverpeas-ldap.ldif) that we have declared and should be in src/test/resources.
  • the directory opendj which contains the minimum confiuration to run OpenDJ and should also be insrc/test/resources.

Be careful not to filter the content of the opendi directory with Apache Maven.
The source code with a sample is available on Github : https://github.com/ehsavoie/embedded-ldap

Many thanks to Ludovic Poitou for his kind help on how to configure and use OpenDJ.