JUnit 4: AEM Sling Servlet Unit Test Example Using wcm.io AEM Mocks, Servlet by Resource Type

This article will demonstrate how to write AEM Unit tests for @SlingServletResourceTypes (OSGi DS 1.4 (R7) component property type annotations) using the Junit4 testing framework. With developers being more visual, the source code is posted below.

Technologies here used are:

  • AEM project archetype 19 (link)
  • Mockito 2.27.0 (link)
  • AEM Mocks JUnit 4 2.7.2 (link)

This example uses the AEM project archetype 19 to generate a new AEM project, Junit 4 will be used as the testing framework, Mockito 2.27.0 will be used as the mocking framework, and AEM Mocks will be used to mock AEM objects and AEM API.

What’s really great about the latest versions of AEM mocks, is that the setup is very minimal. After spinning up a new AEM project from the AEM project archetype 19, you simply need to include the AEM Mocks dependency, and you are ready to go!

Using OSGI R7 annotations for your AEM project requires some additional dependencies. Check out Adobe’s guide to enable OSGI R7 annotations.

Test Framework Dependencies

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// pom.xml
<!-- Maven Surefire Plugin -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.20</version>
    <configuration>
        <junitArtifactName>junit:junit:4</junitArtifactName>
    </configuration>
</plugin>
...
<dependencies>
    ...
    <dependency>
        <groupId>io.wcm</groupId>
        <artifactId>io.wcm.testing.aem-mock</artifactId>
        <version>2.7.2</version>
        <scope>test</scope>
    </dependency>
    ...
</dependencies>

// core/pom.xml
<dependencies>
    ...
    <dependency>
        <groupId>io.wcm</groupId>
        <artifactId>io.wcm.testing.aem-mock</artifactId>
    </dependency>
    ...
</dependencies>

Sling Servlet Resource Types Class : SlingServletResourceTypes.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package com.sourcedcode.core.servlets;

import com.day.cq.wcm.api.Page;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.servlets.annotations.SlingServletResourceTypes;
import org.osgi.service.component.annotations.Component;

import javax.servlet.Servlet;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import static com.day.crx.packaging.JSONResponse.APPLICATION_JSON_UTF8;
import static org.apache.sling.api.servlets.HttpConstants.METHOD_GET;

/**
 * A demonstration sling servlet resource type class to demonstrate unit testing.
 * Appending ".example.json" on any resource will activate the doGet() method below, and return Json data.
 * Json Data returned is data from the immediate children of the "/content/we-retail', parent resource node.
 */

@Component(service = Servlet.class)
@SlingServletResourceTypes(
        resourceTypes = "sling/servlet/default",
        methods = METHOD_GET,
        extensions = "json",
        selectors = "example")
public class SlingServletResourceTypesExampleDS14Servlet extends SlingSafeMethodsServlet {

    @Override
    protected void doGet(SlingHttpServletRequest req, SlingHttpServletResponse res) throws IOException {
        res.setContentType(APPLICATION_JSON_UTF8);
        res.setStatus(SlingHttpServletResponse.SC_OK);
        List<PageItem> pageItems = getPageItems(req);
        String json = new ObjectMapper().writeValueAsString(pageItems);
        res.getWriter().write(json);
    }

    private List<PageItem> getPageItems(SlingHttpServletRequest req) {
        List<PageItem> pageItems = new ArrayList<>();
        ResourceResolver resolver = req.getResourceResolver();
        Resource weRetail = resolver.getResource("/content/we-retail");
        if (weRetail != null) {
            Iterator<Resource> it = weRetail.listChildren();
            while (it.hasNext()) {
                Resource resource = it.next();
                if (resource.getResourceType().equalsIgnoreCase(com.day.cq.wcm.api.NameConstants.NT_PAGE)) {
                    Page page = resource.adaptTo(Page.class);
                    if (page != null) {
                        pageItems.add((new PageItem(page.getTitle(), resource.getPath())));
                    }
                }
            }
        }
        return pageItems;
    }

    /**
     * Inner class PageItem, for creating PageItem objects.
     */

    public class PageItem {

        private String pageTitle;

        private String pagePath;

        PageItem(String pageTitle, String pagePath) {
            this.pageTitle = pageTitle;
            this.pagePath = pagePath;
        }

        public String getPageTitle() {
            return pageTitle;
        }

        public String getPagePath() {
            return pagePath;
        }
    }
}

Results of the output from the working as expected SlingServletResourceTypesTest.class.
Sling Servlet Resource TypeResults Example

Sling Servlet Resource Types Test Class : SlingServletResourceTypesTest.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package com.sourcedcode.core.servlets;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.wcm.testing.mock.aem.junit.AemContext;
import org.apache.sling.testing.mock.sling.ResourceResolverType;
import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletRequest;
import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletResponse;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import java.io.IOException;

import static com.day.crx.packaging.JSONResponse.APPLICATION_JSON_UTF8;
import static junitx.framework.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

@RunWith(MockitoJUnitRunner.class)
public class SlingServletResourceTypesExampleDS14ServletTest {

    @Rule
    public final AemContext context = new AemContext(ResourceResolverType.JCR_MOCK);

    @Mock
    private MockSlingHttpServletRequest req;

    @Mock
    private MockSlingHttpServletResponse res;

    @InjectMocks
    private SlingServletResourceTypesExampleDS14Servlet underTest;

    @Before
    public void setup() {
        underTest = new SlingServletResourceTypesExampleDS14Servlet();
        req = context.request();
        res = context.response();
    }

    @Test
    public void doGet_shouldReturnHeaderAsExpected() throws IOException {
        underTest.doGet(req, res);
        assertEquals(res.getContentType(), APPLICATION_JSON_UTF8);
    }

    @Test
    public void doGet_shouldReturnPageItemListJsonAsExpected_0() throws IOException {
        underTest.doGet(req, res);
        String jsonString = res.getOutputAsString();
        ObjectMapper mapper = new ObjectMapper();
        JsonNode actualObj = mapper.readTree(jsonString);
        assertEquals(0, actualObj.size());
    }

    @Test
    public void doGet_shouldReturnPageItemJsonAsExpected_properties_values() throws IOException {
        createPagesInJcrMock();
        underTest.doGet(req, res);
        String jsonString = res.getOutputAsString();
        ObjectMapper mapper = new ObjectMapper();
        JsonNode actualObj = mapper.readTree(jsonString);
        JsonNode firstItem = actualObj.get(0);
        assertTrue(firstItem.has("pageTitle"));
        assertTrue(firstItem.has("pagePath"));
        assertEquals( "United states", firstItem.get("pageTitle").textValue());
        assertEquals("/content/we-retail/us", firstItem.get("pagePath").textValue());
    }

    /**
     * Test helper method to create 3 pages in the on-memory JCR Mock instance.
     */

    private void createPagesInJcrMock() {
        context.create().page("/content/we-retail", "/", "We Retail");
        context.create().page("/content/we-retail/us", "/", "United states");
        context.create().page("/content/we-retail/ca", "/", "Canada");
    }
}
Last Thoughts:
Though the endpoint path is ugly (because it consists of extensions, selectors, and etc…), it is recomended to register servlet by resource types rather than by path (click here to learn why @SlingServletPaths, register by path, are not recommended).

If there is a requirement to mask or to sugarcoat the ugly URI paths, a common strategy used is the Apache webserver. Click here to learn to sugar coat registered AEM servlet scripts and paths endpoint.

Was this Article Helpful?

Certified AEM Developer who has been working on AEM software developer for the past 5 years.

Leave a Reply

Your email address will not be published. Required fields are marked *

Back To Top