Tech Tips

AEM Tip: JUnit AemContext Integration with an AEM Repository

It’s difficult to imagine a modern software application that can live without JUnit tests. In a previous article on JUnit testing, JUnit Tests for WCMUsePojo Objects, we included a tip involving a project we were working on. For this article, we use the same project and extend the approach with sling models and a direct connection to a real AEM repository.

In our case, we encountered a problem when we had to test a big back-end service that had a lot of dependencies with other services, data, and web content inside a JCR repository. Creating JUnit tests for AEM components and services requires preparing tons of data, so we implemented a solution that helps save time in preparing all of this data. We were able to use a real repository and data prepared in AEM using all of AEM’s capabilities. This solution significantly sped up writing tests and improved the quality of tests.

Connecting a JUnit test to AEM is a no-brainer requiring just a few simple steps:

  1. Download a class package https://github.com/apache/sling-org-apache-sling-testing-sling-mock-oak/tree/master/src/main/java/org/apache/sling/testing/mock/sling/oak
  2. Include the WEB DAV repository factory org.apache.jackrabbit:jackrabbit-jcr2dav dependency in your pom.xml file
  3. Modify the OakMockSlingRepository class from the first step to use the ProxyRepository Connection. Here’s just the changed code:
private SimpleCredentials simpleCredentials;
    private ProxyRepository proxyRepository;
 
@Activate
    protected void activate(BundleContext bundleContext) {
        executor = Executors.newSingleThreadExecutor();
        scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
 
        if (bundleContext.getServiceReference(Executor.class) == null) {
            bundleContext.registerService(Executor.class, executor, null);
        }
 
        try {
            simpleCredentials = new SimpleCredentials("[AEM user]", “[password]”);
            proxyRepository = new ProxyRepository(mode.getPath() + "/crx/server");
 
            Session session = proxyRepository.login(simpleCredentials);
            repository = session.getRepository();
        } catch (Exception e) {
        //TODO
        }
    }
    public Session loginAdministrative(String workspaceName) throws RepositoryException {
        return proxyRepository.login(simpleCredentials);//you possibly want to use admin creds
    }
See more See less

Now you can declare an AemContext Test Rule in your test cases:

@Rule
public AemContext context = new AemContext(ResourceResolverType.JCR_OAK);
See more See less

It will use your AEM Repository for tests. Almost anything is possible now, just use a real repository path and adapt to page or sling model:

@Test
public void slingModelTest() throws IOException {    
        Resource resource = context.resourceResolver().
             getResource("/content/blueprint/junit/page");
        Page page = resource.adaptTo(Page.class);
        ComponentSlingModel model = page.getContentResource().
             getChild("main-par/buttoncompone").adaptTo(ComponentSlingModel.class);
//TODO
}
See more See less

Tips for Additions

Here is a list of nice tips for additions to your tests’ base class that can help you with complex tests:

OSGi configs for the test context 

ConfigurationAdmin сonfigurationAdmin = context.getService(ConfigurationAdmin.class);
Dictionary<String, Object> properties = new Hashtable<String, Object>();
properties.put("[key]", "value");
сonfigurationAdmin.getConfiguration("configuration PID").update(properties);
ConfigurationAdmin сonfigurationAdmin = context.getService(ConfigurationAdmin.class);
Dictionary<String, Object> properties = new Hashtable<String, Object>();
properties.put("[key]", "value");
сonfigurationAdmin.getConfiguration("configuration PID").update(properties);
See more See less

Run modes for the test context 

MockSlingSettingService settingService = (MockSlingSettingService)
context.getService(SlingSettingsService.class);
Set<String> runModes = new HashSet<>();
Collections.addAll(runModes, "author", "qa");
settingService.setRunModes(runModes);
See more See less

Add package with models (useful examples for ACS lists)

context.addModelsForPackage("com.adobe.acs.commons.genericlists"); context.addModelsForClasses(GenericList.class);
See more See less

Inject and Activate services (any of your real services or external services like GenericListAdapterFactory)

context.registerInjectActivateService(new
         com.adobe.acs.commons.genericlists.impl.GenericListAdapterFactory());
context.registerInjectActivateService(new MyCacheServiceImpl());
See more See less

Register any AEM Mock Service without activation, just to satisfy dependencies

context.registerService(CryptoSupport.class, new MockCryptoSupport());
See more See less

Supply the context and sling models with sling request related objects

Resource mockRequestRes = context.resourceResolver().getResource("/[path]/anypage");
context.request().setResource(resource);
context.request().setServletPath("[sevlet path]");
 
Page page = anyResource.adaptTo(Page.class);
anyExampleModel.setRequest(context.request());
anyExampleModel.setCurrentPage(page);
anyExampleModel.setSlingScriptHelper(context.slingScriptHelper());
See more See less

Author: Peter Zhuravlev