Unitils – library for enhance unit test by using annotation approach
In the post Enhance the Unit test using Annotation approach, I outlined the approach of using annotation to reduce the redundant code and avoid the inheritance (like JUnit3). Annotation RunWith of JUnit4 is a powerful class help us to custom the approach of running unit test, we can create the class inherits from class TestRunner. Unitils is the unit test framework for Spring, Hibernate and Database testing but it provide the mechanism to enhance unit testing via annotation by using its modules. Actually, I do not use the standard modules provide by Unitils like Hibernate mapping testing, Spring bean testing but I mostly write new modules to serve my testing purposes. In this post, I will give an example of extending Unitils for Ldap unit test purpose.
Scenario: I would like to test login function of some application use Ldap server as user information database. The unit test will launch the embedded LdapServer in each test method as the excellent guideline describe here http://directory.apache.org/apacheds/1.5/using-apacheds-for-unit-tests.html. Typically, I would like each test class launch embedded ldap server use the annotation EmbeddingServer:
@RunWith
(UnitilsJUnit4TestClassRunner.class)
@EmbeddingLdapServer
public classRoleLdapServiceTest {
@Test
public voidtestFindRole() {
Assert.assertNotNull(roleLdapService.findRoleName("ROLE_ADMINISTRATOR"));
Assert.assertNull(roleLdapService.findRoleName ("ROLE_TEST"));
...
}
...
}
To add the new module to Unitils is very simple:
- Add the new module name in unitils.properties (you can see how to use unitils.properties to configure modules at http://unitils.sourceforge.net/tutorial.html#Unitils%20Modules):
unitils.modules=database,dbunit,hibernate,easymock,inject,spring, ldap (Note that database, dbunit, hibernate, easymock,inject and spring are the built-in modules of Unitils, and I add ldap module for my ldap unit testing). - Add the related information for initializing our module in the same unitils.properties file
unitils.module.ldap.className=org.nextss.util.test.ldap.LdapModule
unitils.module.ldap.enabled=true
ldap.username=uid=admin,ou=system
ldap.password=secret
ldap.authentication.method=simple
ldap.factory.classname=org.apache.directory.server.jndi.ServerContextFactory - Create the annotation class that new module supports
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interfaceEmbeddingLdapServer {
}EmbeddingLdapServer is annotated to class and it is retained in runtime as we define in requirement
- Create Module class corresponds the new module. This class must inherit the interface org.unitils.core.Module and override two methods void init(Properties configuration) and TestListener createTestListener(). The usage of these two classes are:
- init is used to load any pre-defined configuration of defined module (in unitils.properties file)
- createTestListener creates the TestListener for test events with the following orders: -
- [Unitils] beforeAll
- [Unitils] beforeTestClass - TestClass1
- [Test] testBeforeClass - TestClass1 (not for JUnit3)
- [Unitils] beforeTestSetUp - TestClass1
- [Test] testSetUp - TestClass1
- [Unitils] beforeTestMethod - TestClass1 - test1
- [Test] testMethod - TestClass1 - test1
- [Unitils] afterTestMethod - TestClass1 - test1
- [Test] testTearDown - TestClass1
- [Unitils] afterTestTearDown - TestClass1
- [Unitils] beforeTestSetUp - TestClass1
- [Test] testSetUp - TestClass1
- [Unitils] beforeTestMethod - TestClass1 - test2
- [Test] testMethod - TestClass1 - test2
- [Unitils] afterTestMethod - TestClass1 - test2
- [Test] testTearDown - TestClass1
- [Unitils] afterTestTearDown - TestClass1
- [Test] testAfterClass - TestClass1 (not for JUnit3)
- [Unitils] afterTestClass - TestClass1
- [Unitils] afterAll
In our case, we write the new module names LdapListener. Because, we want unitils read the annotation EmbeddingLdapServer at the beginning of running test class, we will override the method beforeTestClass:
@Override
public void beforeTestClass(Class<?> testClass) {
EmbeddingLdapServerclassAnnotation = getClassLevelAnnotation(EmbeddingLdapServer.class, testClass);
if(classAnnotation != null) {
log.debug("before test class");
serverOnline= false;
doDelete= true;
configuration= newLdapServerConfigDecorator();
port= -1;
}
super.beforeTestClass(testClass);
}
Next, we setup the partition'nextss' for ldapServer in method beforeTestSetUp after ldap server is started:
@Override
public void beforeTestSetUp(Object testObject) {
EmbeddingLdapServerclassAnnotation = getClassLevelAnnotation(EmbeddingLdapServer.class, testObject.getClass());
if(classAnnotation != null) {
// Add partition 'nextss'
MutablePartitionConfiguration pcfg = newMutablePartitionConfiguration();
pcfg.setId("nextss");
try{
pcfg.setSuffix("dc=nextss");
} catch(NamingException e) {
throw new RuntimeException(e);
}
....
}
After creating the partition, we import the data by using ldif file:
@Override
public void beforeTestMethod(Object testObject, Method testMethod) {
Class<?> testClass = testObject.getClass();
EmbeddingLdapServerclassAnnotation = getClassLevelAnnotation(EmbeddingLdapServer.class, testClass);
if(classAnnotation != null) {
try{
setUp();
} catch(Exception e) {
throw new RuntimeException(e);
}
InputStream ldifStream = testClass.getResourceAsStream(testClass.getSimpleName()+ ".ldif");
if(ldifStream == null) {
throw new NextssException("File: "+ testClass.getSimpleName() + ".ldif is not found");
}
try{
importLdif(ldifStream);
} catch(NamingException e) {
throw new NextssException(e);
}
}
super.beforeTestMethod(testObject, testMethod);
}
And the last, not miss to clean up data after each test method
:
@Override
public void afterTestMethod(Object testObject, Method testMethod,Throwable testThrowable) {
EmbeddingLdapServerclassAnnotation = getClassLevelAnnotation(EmbeddingLdapServer.class, testObject.getClass());
if(classAnnotation != null) {
try{
tearDown();
} catch(Exception e) {
throw new RuntimeException();
}
}
super.afterTestMethod(testObject, testMethod, testThrowable);
}
It is very simple, right?
. You can start writing test with Embedded Ldap Server will run and import data before each test method.
UserLdapServiceTest.javaLdapModule.javaLdapServerConfigDecorator.javaEmbeddingLdapServer.java