AEM Tip: JUnit Tests for WCMUsePojo Objects

Exadel Digital Experience Team Tech Tips September 9, 2019 17 min read

When we develop AEM components, we add some logic into them. This logic can include a variety of source code: helpers, utils, or code designed specially for a component. After developing a number of components, we need to verify the logic. Any refactoring, any changes in helper and util classes, requires us to validate existing source code, thus verifying that the code is in the expected state. JUnit tests help us do this.

However, in the process of developing the JUnit tests, a few issues arose. For instance: WCMUsePojo (a base class for components) has many static methods which have to be mocked to perform testing. We have to load content and test our component against this content to validate its logic.

For our unit tests, we use an open source project called wcm.io as well as PowerMock. Wcm.io provides various useful AEM mock objects. PowerMock helps to override static and final methods in classes for mocking. PowerMock takes precedence over the Mockito framework in this process.

Let’s imagine that this is our component class:

public class OurSuperComponent extends WCMUsePojo {
 
}
See more See less

To write a unit test for a class, the first thing we will do is annotate a test class with the following annotations:

@RunWith(PowerMockRunner.class)
@PrepareForTest({OurSuperComponent.class, RequestHelper.class})
public class OurSuperComponentTest extends ComponentBaseTest  
{}
See more See less

@RunWith specifies a runner for JUnit; @PrepareForTest lists a number of classes which have final or static methods to mock.

Here is the component base class:

public abstract class ComponentBaseTest {
 
protected final Class componentClass = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
 
protected final AemContext context = new AemContext(ResourceResolverType.RESOURCERESOLVER_MOCK);
 
protected T component;
protected Resource resource;
 
@Before
public void setUp() throws Exception {
// all component’s methods are mock by default
   component = PowerMockito.mock(componentClass);
   resource = PowerMockito.mock(Resource.class);
}
}
See more See less

AemContext is a useful mock object. It could contain test content.  Here is a document describing how to load it.

Next we can get ResourceResolver.

ResourceResolver resourceResolver = context.resourceResolver();
See more See less

And mock it for further use while testing in a component:

when(resource.getResourceResolver()).thenReturn(resourceResolver);
when(component.getResourceResolver()).thenReturn(resourceResolver);
when(slingHttpServletRequest.getResourceResolver()).thenReturn(resourceResolver);
See more See less

In the base class we mock our test component:

component = PowerMockito.mock(componentClass);
See more See less

Now all its methods are mocked. To call a real method to test it, we would write:

PowerMockito.doCallRealMethod().when(component).activate();
See more See less

If a test method is private and we have to test it or call it, we can write:

PowerMockito.when(component,"getItem",anyObject()).thenCallRealMethod();
See more See less

How to mock a static method?

For instance, let’s say we have a util class with a static method which is used in our component:

public final class LinkUtil {
public static String localizeUrl(final ResourceResolver resourceResolver, final String currentPagePath, final String link) {
// method body
}
}
See more See less

In a unit test we would write:

PowerMockito.mockStatic(LinkUtil.class);
when(LinkUtil.localizeUrl(anyObject(), anyString(), anyString())).thenReturn("/content/page.html");
See more See less

PowerMockito.mockStatic mocks all the methods in a specified class. If you want to mock one method, use PowerMockito.spy.

Source Code

1. Component Class

public class MySuperComponent extends WCMUsePojo {
 
   private String title;
   private String upperTitle;
   private String resourcePath;
 
   @Override
   public void activate() throws Exception {
       title = getProperties().get("./title", StringUtils.EMPTY);
       resourcePath = getProperties().get("./resourcePath", StringUtils.EMPTY);
       upperTitle = getUpperString(title);
   }
 
   private String getUpperString(String title) {
       if (StringUtils.isNotBlank(title)) {
           return StringUtils.upperCase(title);
       }
       return title;
   }
 
   public String getTitle() {
       return title;
   }
 
   public String getUpperTitle() {
       return upperTitle;
   }
 
   public String getResourcePath() {
       return resourcePath;
   }
 
   public String getCurrentPagePath() {
       return getCurrentPage().getPath();
   }
 
   public String getResourceTitle() {
       Resource resource = getResourceResolver().getResource(resourcePath + "/jcr:content");
       return resource.getValueMap().get("title").toString();
   }
}
See more See less

2. Base Test Class

public abstract class ComponentBaseTest {
 
   protected T component;
   protected ValueMap valueMap;
   protected SlingScriptHelper slingScriptHelper;
   protected SlingHttpServletRequest slingHttpServletRequest;
   protected ResourceResolver resourceResolver;
   protected SightlyWCMMode sightlyWCMMode;
   protected Resource resource;
   protected MockRequestPathInfo requestPathInfo;
   private boolean firstRun = true;
 
   protected final Class componentClass = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
 
   protected final AemContext context = new AemContext(ResourceResolverType.RESOURCERESOLVER_MOCK);
 
   protected void runOnceBeforeTests() {}
 
   @Before
   public void setUp() throws Exception {
       runOnce();
       component = PowerMockito.mock(componentClass);
       resource = PowerMockito.mock(Resource.class);
 
       resourceResolver = context.resourceResolver();
       valueMap = new ValueMapDecorator(new HashMap<>());
 
       slingScriptHelper = PowerMockito.mock(SlingScriptHelper.class);
       slingHttpServletRequest = PowerMockito.mock(SlingHttpServletRequest.class);
       sightlyWCMMode = PowerMockito.mock(SightlyWCMMode.class);
 
       when(component.getResource()).thenReturn(resource);
       when(resource.getResourceResolver()).thenReturn(resourceResolver);
       when(component.getResourceResolver()).thenReturn(resourceResolver);
 
       when(component.getProperties()).thenReturn(valueMap);
       when(component.getSlingScriptHelper()).thenReturn(slingScriptHelper);
       when(component.getRequest()).thenReturn(slingHttpServletRequest);
       when(component.getWcmMode()).thenReturn(sightlyWCMMode);
 
       requestPathInfo = new MockRequestPathInfo();
       when(slingHttpServletRequest.getRequestPathInfo()).thenReturn(requestPathInfo);
       when(slingHttpServletRequest.getResourceResolver()).thenReturn(resourceResolver);
   }
 
   private void runOnce() {
       if (firstRun) {
           firstRun = false;
           runOnceBeforeTests();
       }
   }
}
See more See less

3. JUnit Test Class

@RunWith(PowerMockRunner.class)
@PrepareForTest({ MySuperComponent.class, StringUtils.class})
public class MySuperComponentTest extends ComponentBaseTest {
 
   @Override
   protected void runOnceBeforeTests() {
       initContent();
   }
 
   public void initContent() {
       context.load().json("/test/TestNode.json", "/content/us/en/testPage/jcr:content");
   }
 
   @Test
   public void testActivate() throws Exception {
       PowerMockito.doCallRealMethod().when(component).activate();
       PowerMockito.when(component, "getUpperString", anyString()).thenCallRealMethod();
 
       PowerMockito.doCallRealMethod().when(component).getTitle();
       PowerMockito.doCallRealMethod().when(component).getResourcePath();
 
       final String resourcePathTest = "/content/us/en/testPage";
       final String titleTest = "SimpleTitle";
       valueMap.put("./resourcePath", resourcePathTest);
       valueMap.put("./title", titleTest);
 
       component.activate();
 
       assertEquals(resourcePathTest, component.getResourcePath());
       assertEquals(titleTest, component.getTitle());
 
   }
 
   @Test
   public void testPrivateMethodGetUpperString() throws Exception {
       PowerMockito.doCallRealMethod().when(component).activate();
       PowerMockito.doCallRealMethod().when(component).getUpperTitle();
       PowerMockito.when(component, "getUpperString", anyString()).thenCallRealMethod();
 
       final String titleTest = "SimpleTitle";
       valueMap.put("./title", titleTest);
 
       component.activate();
       //returns in upper case
       assertEquals(titleTest.toUpperCase(), component.getUpperTitle());
 
       // Static mock StringUtils
       PowerMockito.mockStatic(StringUtils.class);
       // mock all methods in StringUtils
       when(StringUtils.isNotBlank(anyString())).thenReturn(Boolean.FALSE);
 
       component.activate();
       // returns the result string in lower case
       assertEquals(titleTest, component.getUpperTitle());
 
       // Partial static mock StringUtils
       PowerMockito.spy(StringUtils.class);
       // mock only the following methods in StringUtils
       when(StringUtils.upperCase(anyString())).thenReturn("HackedString");
 
       component.activate();
       // returns the mocked string
       assertEquals("HackedString", component.getUpperTitle());
   }
 
   @Test
   public void testGetResourceTitle() throws Exception {
       PowerMockito.doCallRealMethod().when(component).activate();
       PowerMockito.doCallRealMethod().when(component).getResourceTitle();
 
       // Necessary test page content has been loaded in initContent method
       final String resourcePathTest = "/content/us/en/testPage";
       valueMap.put("./resourcePath", resourcePathTest);
 
       component.activate();
 
       String result = component.getResourceTitle();
       assertEquals("Resource test title", result);
   }
}
See more See less

4. Test Page Content [TestNode.json]

{
 "jcr:primaryType": "cq:PageContent",
 "title": "Resource test title",
 "jcr:title": "Page title"
}
See more See less

Author: Alexander Bestsenny

Was this article useful for you?

Get in the know with our publications, including the latest expert blogs