When 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.
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 GitHub repository, fork it there or clone it using Git:
git clone https://github.com/hascode/ldap-junit-testing-tutorial.git
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.