LDAP Testing with Java: ApacheDS vs Embedded-LDAP-JUnit
July 4th, 2016 by Micha KopsWhen writing applications that interchange information with LDAP directory services there is always the need to write integration tests for these components and services.
Therefore we need a the possibility to start-up an embedded LDAP server, fill it with test-data and control its life-cycle during the test-phases.
In the following tutorial I’d like to demonstrate two candidates that fulfil this purpose, the ApacheDS test integrations and a small library named embedded-ldap-junit.
Contents
Test Data
To populate our directory servers with some test data, we’re using the following LDIF file providing organization structures and persons taken from my old tutorial “Creating a LDAP server for your development environment in 5 minutes“.
This is our users-import.ldif used:
dn: dc=example,dc=com objectClass: domain objectClass: top dc: example dn: ou=Users,dc=example,dc=com objectClass: organizationalUnit objectClass: top ou: Users dn: ou=Groups,dc=example,dc=com objectClass: organizationalUnit objectClass: top ou: Groups dn: cn=Micha Kops,ou=Users,dc=example,dc=com objectClass: inetOrgPerson objectClass: organizationalPerson objectClass: person objectClass: top cn: Micha Kops sn: Kops uid: mkops userPassword: abcdefg dn: cn=Santa Claus,ou=Users,dc=example,dc=com objectClass: top objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson cn: Santa Claus sn: Claus uid: sclaus userPassword: abcdefg dn: cn=John Steinbeck,ou=Users,dc=example,dc=com objectClass: top objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson cn: John Steinbeck sn: Steinbeck uid: jsteinbeck userPassword: abcdefg
Embedded LDAP JUnit
Though being in an early stage of development, embedded-ldap-junit looks quite nice and provides us a JUnit rule for running an embedded LDAP server in your JUnit test and it is based on the UnboundID LDAP SDK.
Dependencies
We just need to add one dependency to our Maven project’s pom.xml:
<dependency> <groupId>org.zapodot</groupId> <artifactId>embedded-ldap-junit</artifactId> <version>0.5.2</version> <scope>test</scope> </dependency>
Integration Test
This is what our integration test looks like.
The EmbeddedLdapRuleBuilder allows us to construct our JUnit rule, to import our test data from the LDIF file and to open a connection to this server.
package it; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import java.util.List; import org.junit.Rule; import org.junit.Test; import org.zapodot.junit.ldap.EmbeddedLdapRule; import org.zapodot.junit.ldap.EmbeddedLdapRuleBuilder; import com.unboundid.ldap.sdk.LDAPInterface; import com.unboundid.ldap.sdk.SearchResult; import com.unboundid.ldap.sdk.SearchResultEntry; import com.unboundid.ldap.sdk.SearchScope; /** * Test using objectclass: org.zapodot:embedded-ldap-junit */ public class EmbeddedLdapTest { public static final String DOMAIN_DSN = "dc=example,dc=com"; @Rule public EmbeddedLdapRule embeddedLdapRule = EmbeddedLdapRuleBuilder.newInstance().usingDomainDsn(DOMAIN_DSN) .importingLdifs("users-import.ldif").build(); @Test public void shouldFindAllPersons() throws Exception { final LDAPInterface ldapConnection = embeddedLdapRule.ldapConnection(); final SearchResult searchResult = ldapConnection.search(DOMAIN_DSN, SearchScope.SUB, "(objectClass=person)"); assertThat(3, equalTo(searchResult.getEntryCount())); List<SearchResultEntry> searchEntries = searchResult.getSearchEntries(); assertThat(searchEntries.get(0).getAttribute("cn").getValue(), equalTo("John Steinbeck")); assertThat(searchEntries.get(1).getAttribute("cn").getValue(), equalTo("Micha Kops")); assertThat(searchEntries.get(2).getAttribute("cn").getValue(), equalTo("Santa Claus")); } @Test public void shouldFindExactPerson() throws Exception { final LDAPInterface ldapConnection = embeddedLdapRule.ldapConnection(); final SearchResult searchResult = ldapConnection.search("cn=Santa Claus,ou=Users,dc=example,dc=com", SearchScope.SUB, "(objectClass=person)"); assertThat(1, equalTo(searchResult.getEntryCount())); assertThat(searchResult.getSearchEntries().get(0).getAttribute("cn").getValue(), equalTo("Santa Claus")); } }
Running the Test
We may run our tests now using our IDE of choice or via Maven in the command line.
$ mvn -Dtest=EmbeddedLdapTest test [..] ------------------------------------------------------- T E S T S ------------------------------------------------------- Running it.EmbeddedLdapTest Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.588 sec Results : Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.016s
ApacheDS
ApacheDS is an extensible and embeddable, LDAPv3 compatible directory server.
In addition to the server, testing tools are also provided and there’s a detailed documentation available how to write tests using ApacheDS.
Dependencies
We need some more dependencies here and we’re adding some exclusions to avoid classpath conflicts with additional LDIF schema files.
<dependency> <groupId>org.apache.directory.server</groupId> <artifactId>apacheds-all</artifactId> <version>2.0.0-M22</version> <exclusions> <!-- exclude additional LDIF schema files to avoid conflicts through multiple copies --> <exclusion> <groupId>org.apache.directory.shared</groupId> <artifactId>shared-ldap-schema</artifactId> </exclusion> <exclusion> <groupId>org.apache.directory.api</groupId> <artifactId>api-ldap-schema-data</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.directory.server</groupId> <artifactId>apacheds-server-integ</artifactId> <version>2.0.0-M22</version> <exclusions> <!-- exclude additional LDIF schema files to avoid conflicts through multiple copies --> <exclusion> <groupId>org.apache.directory.shared</groupId> <artifactId>shared-ldap-schema</artifactId> </exclusion> <exclusion> <groupId>org.apache.directory.api</groupId> <artifactId>api-ldap-schema-data</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.directory.server</groupId> <artifactId>apacheds-core-integ</artifactId> <version>2.0.0-M22</version> <exclusions> <!-- exclude additional LDIF schema files to avoid conflicts through multiple copies --> <exclusion> <groupId>org.apache.directory.shared</groupId> <artifactId>shared-ldap-schema</artifactId> </exclusion> <exclusion> <groupId>org.apache.directory.api</groupId> <artifactId>api-ldap-schema-data</artifactId> </exclusion> </exclusions> </dependency>
Integration Test
This is our final integration test. We may configure our server or multiple servers using annotations on class and on method level.
package it; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import javax.naming.NamingEnumeration; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import javax.naming.ldap.Control; import javax.naming.ldap.LdapContext; import javax.naming.ldap.SortControl; import org.apache.directory.server.annotations.CreateLdapServer; import org.apache.directory.server.annotations.CreateTransport; import org.apache.directory.server.core.annotations.ApplyLdifFiles; import org.apache.directory.server.core.annotations.CreateDS; import org.apache.directory.server.core.annotations.CreatePartition; import org.apache.directory.server.core.integ.AbstractLdapTestUnit; import org.apache.directory.server.core.integ.FrameworkRunner; import org.apache.directory.server.integ.ServerIntegrationUtils; import org.junit.Test; import org.junit.runner.RunWith; /** * Test using apacheds */ @RunWith(FrameworkRunner.class) @CreateLdapServer(transports = { @CreateTransport(protocol = "LDAP") }) @CreateDS(allowAnonAccess = true, partitions = { @CreatePartition(name = "Example Partition", suffix = "dc=example,dc=com") }) @ApplyLdifFiles("users-import.ldif") public class ApacheDsLdapTest extends AbstractLdapTestUnit { @Test public void shouldFindAllPersons() throws Exception { LdapContext ctx = (LdapContext) ServerIntegrationUtils.getWiredContext(ldapServer, null) .lookup("ou=Users,dc=example,dc=com"); // we want a sorted result, based on the canonical name ctx.setRequestControls(new Control[] { new SortControl("cn", Control.CRITICAL) }); NamingEnumeration<SearchResult> res = ctx.search("", "(objectClass=person)", new SearchControls()); assertThat(res.hasMore(), equalTo(true)); assertThat(res.next().getName(), equalTo("cn=John Steinbeck")); assertThat(res.next().getName(), equalTo("cn=Micha Kops")); assertThat(res.next().getName(), equalTo("cn=Santa Claus")); } }
Running the Test
We may run our tests now using our IDE of choice or via Maven in the command line.
$ mvn -Dtest=ApacheDsLdapTest test [..] ------------------------------------------------------- T E S T S ------------------------------------------------------- Running it.ApacheDsLdapTest Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 5.719 sec Results : Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 7.173s
Tutorial Sources
Please feel free to download the tutorial sources from my Bitbucket repository, fork it there or clone it using Git:
git clone https://bitbucket.org/hascode/ldap-junit-testing-tutorial.git
Resources
Setting up an ApacheDS Server
I have written a tutorial how to set up an ApacheDS server for testing in no time in my tutorial “Creating a LDAP server for your development environment in 5 minutes” – please feel free to read.
Article Updates
- 2017-06-19: Typo in repository links fixed.
Tags: active directory, ad, Apache, apacheds, ds, embedded-ldap-junit, jndi, junit, ldap, ldif, tdd, test