Unit Testing in Adobe/Day/CQ5
There are a two approaches to effectively test your code in CQ5 – either outside of CQ5 using standard unit tests instantiated from your build tool (i.e. maven). While this approach will cover most simple functionality, the need will quickly arise to execute unit tests within CQ5/Sling/OSGi.
Testing outside of CQ5
In addition to your standard junit tests, you can grab a hold of the CRX repository and start writing some basic tests against that. As you can see in the following example, you ca even wrap the JCR-repository in its Sling equivalent:
package com.jtoee.cq.test;
import org.a~pache.jackrabbit.commons.JcrUtils;
import org.apache.sling.jcr.api.SlingRepository;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
public class SimpleTest {
private Logger log = LoggerFactory.getLogger(SimpleTest.class);
@Test
public void runTest() {
try {
log.debug("Connecting to respository");
Repository repository = JcrUtils.
getRepository("http://localhost:4502/crx/server");
SlingRepository slingRepo =
new RepositoryUtil.RepositoryWrapper(repository);
log.debug("Retrieved sling repository");
// .. do something
} catch (RepositoryException e) {
log.debug("Exception retrieving repository.", e);
}
}
}
Testing within CQ5
If you’ve dug into CQ5 a little more, you’ve noticed the the power of the OSGi container and have probably started writing a lot of components (services) and wired them together via dependency injection. Luckily there’s a very easy way to run unit tests directly in the container and even use the OSGi-style dependency injection directly in your test, allowing you not only to write simple unit tests but also more involved automated integration tests.
According to the Apache Sling testing guideline there are a few approaches but I shall only focus on one of them today.
First, we will need to install a new Sling Junit OSGi bundle into Felix (http://localhost:4502/system/console). Grab it via subversion and compile it:
$ svn co http://svn.apache.org/repos/asf/sling/trunk/testing/junit/core
$ cd core
$ mvn package
Then go into the Felix console (Install/Update button on the right) and install the resulting jar file as a new component (it’s in core/target/org.apache.sling.junit.core-1.0.7-SNAPSHOT.jar):
Now you’re ready to write your first test into one of your custom OSGi bundles. One thing to keep in mind is that while with regular unit tests you would write your tests in the src/test/java directory structure (assuming a standard Maven project layout), you would put these tests into the src/main/java directory – you want them deployed and executed as part of the bundle.
Let’s take a look at a first test:
/**
* <p/>
* User: jochen
* Date: 9/24/11
* Time: 9:08 PM
*/
@RunWith(SlingAnnotationsTestRunner.class)
public class MyAwesomeTest {
@TestReference
private BundleContext bundleContext;
@TestReference
private SlingRepository repository;
@TestReference
private ResourceResolverFactory resourceResolverFactory;
@TestReference
private CloudStorageService storage;
/**
* Get a sling resource resolver
*/
public ResourceResolver getResourceResolver() {
try {
return getResourceResolverFactory().
getAdministrativeResourceResolver(null);
} catch (LoginException e) {
fail(e.toString());
}
return null;
}
@Test
public void testCreateFutureAttachment()
throws Exception {
assertTrue(true);
}
}
As you can see with the class annotation @RunWith(SlingAnnotationsTestRunner.class) we’re telling the system to automatically inject OSGi-dependencies into the test case (you can’t use the regular @Reference annotations you might be using in the rest of your code since these have a compile-time retention policy). Use the @TestReference annotations to inject any of your defined components into your test. The rest of the test is standard Junit.
Maven Setup
Before running the tests, you need to register a regex for the test runner to identify your tests by name, such as the following Maven example does:
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Export-Package>
com.jtoee.cq.test.*
</Export-Package>
<Sling-Test-Regexp>.*Test</Sling-Test-Regexp>
<Private-Package></Private-Package>
<Import-Package>
...
Running Your Tests
The Sling unit testing core package installed earlier includes a simple servlet runner which is available via http://localhost:4502/system/sling/junit/ (adjust to your particular installation). You can simply run the tests from there. The runner implements various selectors, namely .html, .xml, and .json. Especially the XML-based format lends itself toward an easy integration into an automated build process:
$ curl -X POST http://localhost/system/sling/junit/com.jtoee.cq.xml
While I’ve come to love using the the JSON format via curl for readability, it doesn’t give you the stack traces in case any of your tests fail. However, the XML format does.
Conclusion
You have no more excuse not to write unit tests.
