How to set up junit tests with AEM Context in AEM
Goal
Set up junit tests using AEMContext in a AEM project. We will create a generic Abstract Class that will be used to execute junit testing for different use cases (Servlets, Models, Schedulers, etc).
Procedure
First of all, have a look at your POM files. When you create a project using an AEM project archetype, you should already have different dependencies useful for Unit Testing. Since we are using AEM Mocks, please check if these ones are already there, otherwise add them:
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.wcm</groupId>
<artifactId>io.wcm.testing.aem-mock</artifactId>
<version>2.2.8</version>
<scope>test</scope>
</dependency>
Now we can create our Java class. You will find different uses of Mocks. It’s up to you to check whether are used in your project, and if you prefer to include them just in specific classes instead of a generic one.
package com.adobe.training.core.models;
import com.adobe.acs.commons.models.injectors.annotation.impl.AemObjectAnnotationProcessorFactory;
import com.adobe.acs.commons.models.injectors.impl.AemObjectInjector;
import com.adobe.training.core.service.Lab2020Configuration;
import com.adobe.training.core.service.impl.Lab2020ConfigurationImpl;
import com.day.cq.replication.Replicator;
import com.day.cq.search.Query;
import com.day.cq.search.QueryBuilder;
import com.day.cq.search.result.SearchResult;
import io.wcm.testing.mock.aem.junit.AemContext;
import junit.framework.TestCase;
import junitx.util.PrivateAccessor;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.settings.SlingSettingsService;
import org.apache.sling.settings.impl.SlingSettingsServiceImpl;
import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletRequest;
import org.junit.Before;
import org.junit.Rule;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Created by r.teruzzi on 04/01/2018.
*/
@RunWith(MockitoJUnitRunner.class)
public abstract class AbstractBaseTest extends TestCase {
public static final String MODELS_PACKAGE = "com.adobe.training.core.models";
public static final String LAB2020_CONFIGURATION_CONFIG_PATH = "/apps/Lab2020/config/com.adobe.training.core.service.general.impl.Lab2020ConfigurationImpl.config";
public Logger LOG = LoggerFactory.getLogger(getClass());
@Rule
public AemContext aemContext = new AemContext();
AemObjectAnnotationProcessorFactory factory = new AemObjectAnnotationProcessorFactory();
//LIST OF COMMON MOCK OBJECTS YOU MAY USE
@Mock
private ResourceResolverFactory resourceResolverFactory;
@Mock
public SlingSettingsServiceImpl settingsService;
@Mock
protected Replicator replicator;
//IF you have to execute queries
@Mock
public QueryBuilder queryBuilder;
@Mock
public Query query;
@Mock
public SearchResult searchResults;
//LIST OF INJECT MOCKS
@InjectMocks
public AemObjectInjector aemObjectInjector;
//E.G for CUSTOM OSGI SERVICE
@InjectMocks
public Lab2020ConfigurationImpl lab2020Configuration;
//RETRIEVED WITH AEM CONTEXT
public MockSlingHttpServletRequest request;
protected ResourceResolver resourceResolver;
protected BundleContext bundleContext;
@Before
@Override
public void setUp() throws NoSuchFieldException {
aemContext.addModelsForPackage(MODELS_PACKAGE);
request = aemContext.request();
resourceResolver = aemContext.resourceResolver();
bundleContext = aemContext.bundleContext();
loadCommonContent();
registerOsgiConfigurations();
registerServices();
}
protected abstract void loadCommonContent();
//aemContext.load().json("/crxContent/content.json", CONTENT); EXAMPLE - HOW YOU WILL LOAD CONTENT
//Example - How to set OSGI properties
protected void registerOsgiConfigurations() throws NoSuchFieldException {
registerResourceForServices();
Resource configsResource = resourceResolver.getResource(LAB2020_CONFIGURATION_CONFIG_PATH);
ValueMap valueMap = configsResource.getValueMap();
String endpoint1 = valueMap.get("getEndpoint1", String.class);
String endpoint2 = valueMap.get("getEndpoint2", String.class);
PrivateAccessor.setField(this.lab2020Configuration, "endpoint1", endpoint1);
PrivateAccessor.setField(this.lab2020Configuration, "endpoint2", endpoint2);
}
private void registerServices() {
aemContext.registerInjectActivateService(factory);
aemContext.registerService(AemObjectInjector.class, aemObjectInjector);
aemContext.registerService(ResourceResolverFactory.class, resourceResolverFactory);
aemContext.registerService(Lab2020Configuration.class, this.lab2020Configuration);
aemContext.registerService(QueryBuilder.class, queryBuilder);
aemContext.registerService(SlingSettingsService.class, settingsService);
}
private void registerResourceForServices() {
aemContext.load().json("/osgiConfig/lab2020configuration.json", LAB2020_CONFIGURATION_CONFIG_PATH);
}
}
Since unit tests are executed at build, outside the context of a running AEM instance, there is no repository and resources. To facilitate this, wcm.io’s AEM Mocks creates a mock context that allows these APIs to mostly act as if they are running in AEM.
AEM Mocks is a mock implementation of the most important parts of AEM API (Resource Resolver, Resource, Page/PageManager, DAM and so on) and it highly depends on OSGi Mocks, Sling Mocks, JCR Mocks.
A JUnit Rule object “AemContext” gives easy access to all AEM/Sling context objects and allows you to perform operations such as:
- Load content from JSON Files (create your CRX repository)
- Register OSGi services and Sling Models
- Manipulate current request/response states
If you want to know more about AEM Mocks, check this link: https://wcm.io/testing/aem-mock/usage.html
There are different initializations of the AEM Context:
- RESOURCERESOLVER_MOCK (default)
- Mocked Resource Resolver from Sling Testing
- Fastest, but does not support JCR
- Eventing support, but no Search
- JCR_MOCK
- Mocked JCR with real Sling Resource-JCR Mapping
- Still very fast
- Not all JCR features support (e.g. no Search, Observation)
- JCR_OAK
- Real OAK with real Sling Resource-JCR (“CRX3”)
- Full support for Node Types, Search, Observation etc.
- CR_JACKRABBIT
- Real Jackrabbit with real Sling Resource-JCR (“CRX2”)
- Problem: Is not cleaned up for each test run, Tests have to use unique paths and clean up themselves
What you could do, if you need it, is to initialize again the context in your subclass with a more specific resource resolver type!
If would like to know more: https://sling.apache.org/documentation/development/sling-mock.html#resource-resolver-types
Let’s go on through the code. After the AemContext you can see different @Mock and @InjectMocks. Remember that @Mock
creates a mock. @InjectMocks
creates an instance of the class and injects the mocks that are created with the @Mock
.
Behaviors of Mock objects must be modeled by using when
and thenReturn
method.
Then, in the setUp method, we have to:
addModelsForClasses
:
registers the Sling Model to be tested, into the mock AEM Context, so it can be instantiated in the@Test
methods.load().json
: loads resource structures into the mock context, allowing the code to interact with these resources as if they were provided by a real repository. In each class you will override the loadCommonContent method loading the content JSON that more suits for your tests.- registerServices: registers the OSGi services. There is also an example about how to initialize a service with configured properties (registerOsgiConfigurations)
This is how your test folder will look like. The java folder with your test classes, and the resources with the JSON files for your context, and some JSON files used as expected results in the assertEqual conditions, for instance for your servlets tests!

This is just the file used in the example above for the OSGi configuration:
{
"jcr:primaryType": "sling:OsgiConfig",
"getEndpoint1":"/v1/users",
"getEndpoint2":"/v2/users"
}
If you’re wondering which is the related OSGi service, it is taken from here. It’s just used as example.
Now you can start testing all your models, servlets, etc! Here I’m loading all the articles related to Unit Testing.
Cheers! 🍻