Unit Testing AEM Sling Models and Servlets
Published
As teams and code-bases grow, dependencies criss-crossing the code-base become more difficult to track. Unit tests help ensure code modules work consistently and predictably so that any referencing code doesn’t have to.
Code Referred to in this Post
Throughout this post I will be showing snippets of code from the AEM Project Archetype. A few of the other examples are taken from open source projects like ACS AEM Commons and the AEM WCM Core Components. I created generated the archetype code with:
mvn -B archetype:generate \
-D archetypeGroupId=com.adobe.aem \
-D archetypeArtifactId=aem-project-archetype \
-D archetypeVersion=24 \
-D appTitle="AEM Unit Test Demo" \
-D appId="unitdemotest" \
-D groupId="me.callsen.taylor" \
-D aemVersion="6.5.0"
Basic Anatomy of a Sling Model Unit Test
The sample JUnit tests provided as part of the AEM Project Archetype are a great place to start. The HelloWorldModelTest inside of ./core/src/test/java/me.callsen.taylor.core/models
reveals three important components of unit testing.
1. @BeforeEach Functions and Test Setup

The @BeforeEach
function is executed before each unit test is run, and is responsible for setting up the “Testing Context”. This includes loading any test content, creating mock pages/resources, and registering Services and Adapters.
2. Testing Context and the AemContext Object
The Testing Context is where all resources (content, OSGi Services, Adaptables, etc.) are collected and registered for easy use in the unit tests. Think about it like a registry that stores references to each of the external resources; without it, the test wouldn’t know where to find it’s dependencies.
Examples of registering these dependencies can be found below.
3. @Test Execution Functions

Functions annotated with an @Test
contain the actual testing logic. A test should focus on a specific function of the model, and ensure the outcome is the same each time. Similar to other JUnit tests, assertTrue()
and assertEquals()
functions are used to verify function output. Generally a @Test
function is created for each getter function, or any function of a model that executes “controller” type logic.
Mocking AEM Resources
Below are a few techniques to make the various resources used through-out the AEM ecosystem available in unit tests. In that majority of cases, this involves registering content or Classes with the AEM Context Object.
Loading Sample Content
We’ll need come sample content to execute our unit tests against. We can create sample nodes programmatically:
// create a resource and set as currentResource
context.build().resource("/content/myResource", "jcr:title", "My Title").commit();
context.currentResource("/content/myResource");
// create a page and set as currentPage
context.create().page("/content/mysite/en", "/apps/myapp/template/homepage");
context.currentPage("/content/mysite/en");
We can also import content directly from a JSON file, which is helpful when working with larger datasets:
// load json data into a specific location
context.load().json("/my-sample-data.json", "/content/mysite/en");
For more information, see the wcm.io’s content loading documentation here.
Registering an OSGi Service
If the model being tested contains @References
to custom OSGi Services, we’ll need to register these Services with the AEMContext Object. Fortunately the registerService()
function makes this easy to do:
context.registerService(ImplementationPicker.class, new ResourceTypeBasedResourcePicker());
Simply specify the Class of the OSGi @Reference
and supply the Object to be returned. The Testing Context (AemContext Object) will now know which Object to return whenever the @Reference
class is used in a model or unit test.
For more information and examples, check out the official OSGi Mock documentation.
Registering a Sling Adapter
The adaptTo()
interface is one of the more fun-to-use features of AEM and Sling. While common Adaptables are defined in the Testing Context out of the box, custom ones need to be explicitly registered. Fortunately the AemContext object provides the registerAdapter()
function for this purpose:
Session mockSession = mock(Session.class);
context.registerAdapter(ResourceResolver.class, Session.class, mockSession);
In this example, we are using the Mockito Testing Framework to create a mock (skeleton) Session class, and telling the AemContext Object to return this mocked class anytime a ResourceResolved is adapted to a Session (e.g. resourceResolver.adaptTo(Session.class)
).
The AEM WCM Core Components repo has several examples of creating mock classes and registering them as Adaptables.
Mocking Sling Service Functions
Mocking Sling Service function calls can help extend tests to include situations where external service are used, or situations that are too difficult to recreate in a test.
Consider the example below, where the test is dependent on a third-party API. Using Mockit’s collection of functions (e.g. when()
and thenReturn()
), I can easily simulate the API response:
@Mock
private PhotoApiService photoApiService;
@Test
void testPhotoApi(AemContext context) {
when(photoApiService.getPhotoList()).thenReturn(new ArrayList());
List photoList = photoApiService.getPhotoList();
// rest of testing logic
}
The Mockito functions will also apply if the getPhotoList()
call is made inside an included Model or Service, not just inside the test logic.
Testing a Sling Servlet
The AEM Project Archetype has a great example for testing Sling Servlets. Simply open the provided SimpleServletTest inside of ./core/src/test/java/me.callsen.taylor.core/servlets
. Here it is, taken directly from archetype version 24:
@ExtendWith(AemContextExtension.class)
class SimpleServletTest {
private SimpleServlet fixture = new SimpleServlet();
@Test
void doGet(AemContext context) throws ServletException, IOException {
context.build().resource("/content/test", "jcr:title", "resource title").commit();
context.currentResource("/content/test");
MockSlingHttpServletRequest request = context.request();
MockSlingHttpServletResponse response = context.response();
fixture.doGet(request, response);
assertEquals("Title = resource title", response.getOutputAsString());
}
}
The Servlet is loaded as a fixture; after creating a sample resource, a GET request is placed against that resource passing through the the Servlet being tested.
Other Resources
Here are a few resources that can be helpful and provide examples when writing AEM Unit Tests:
- Unit Testing Overview on AEM WKND Tutorial Site – video walk-through of how to set up and execute tests in an AEM projects.
- AEM Mocks Documentation – a suite of Mock resources available for use in AEM unit tests; includes examples and information around the registerService() and registerAdaptables() functions, among other things.
- Further examples of unit tests can be found in the ACS AEM Commons and AEM WCM Code Components repos.
- The JaCoCo Maven Plugin creates visual reports that detail code coverage at a class-level, which can be very handy!
Comments
No responses yet