December 31, 2020
Estimated Post Reading Time ~

Unit Testing Hands on - For WCMUsePojo

This post illustrates the unit test for WCMUsePojo, Java class for backend logic as part of AEM Component development.

Quick Recap about WCMUsePojo:
  • WCMUsePojo initializes the objects associated with Bindings (Eg: WCMBindings/SlingBindings) via its init(Bindings) method which in turn calls the activate() method for post initialization tasks.
  • This init(Bindings bindings) method gets called if the POJO is instantiated in HTL via data-sly-use attribute.
  • Bindings(javax.script.Bindings) extends Map(java.util.Map) and hence it is basically a map object where key (type String)is the global variable and value is the respective object.
  • Once when the bindings are initialized, we are able to make use of the global variables which is available to us via methods like getCurrentPage(), getProperties(), etc.
Example:
When we call getCurrentPage() to get page object, below happens behind the scene (considering the above flow)
  • getCurrentPage() -> bindings.get(WCMBindings.CURRENT_PAGE) where WCMBindings.CURRENT_PAGE or WCMBindingsConstants.NAME_CURRENT_PAGE is a scripting variable/reference variable(currentPage) pointing to Page object.
Given the background on WCMUsePojo, writing a unit test case for the same involves the following
  • Mocking Bindings API (javax.script.Bindings)
  • Dummy implementation for bindings.get() call
  • Create an instance of WCMUsePojo class
  • Call init(mockedBindings) method of WCMUsePojo
  • Call method (under test) of WCMUsePojo
Mocking Bindings API:
Use either @Mock annotation or mock() method from org.mockito.* API
    @Mock
    private Bindings mockBindings;
    or
    Bindings mockBindings = mock(Bindings.class)

Dummy implementation for bindings.get() call
  • Use when and thenReturn methods from org.mockito.* API
  • when(mockBindings.get(WCMBindingsConstants.NAME_CURRENT_PAGE)).thenReturn(pageObj);
  • Where pageObj can be created using mocked page resource definition.
    • Load page resource JSON to dummy content path (aemContext.load() - Content Loader option)
    • Set the aemContext to respective resource/path and hence acquire Page object from it.
Example:
private final String PAGE_CONTENT_PATH = "/content/learnings/en/sample";
private final String PAGE_MOCK_JSON= "/learnings/core/models/SampleWCMUsePojoPageCnt.json";
aemContext.load().json(PAGE_MOCK_JSON, PAGE_CONTENT_PATH);
Resource currentPageResc = aemContext.currentResource(PAGE_CONTENT_PATH);
Page pageObj = aemContext.pageManager().getPage(currentPageResc.getPath());
when(mockBindings.get(WCMBindingsConstants.NAME_CURRENT_PAGE)).thenReturn(pageObj);
                    or
Resource resource = aemContext.resourceResolver().getResource(PAGE_CONTENT_PATH );
Page pageObj = resource.adaptTo(Page.class);
when(mockBindings.get(WCMBindingsConstants.NAME_CURRENT_PAGE)).thenReturn(pageObj);
                    or
Page pageObj = aemContext.create().page(PAGE_CONTENT_PATH );
when(mockBindings.get(WCMBindingsConstants.NAME_CURRENT_PAGE)).thenReturn(pageObj);

Create an instance of WCMUsePojo Class
Let say WCMUsePojo class to be tested is SampleWCMUsePojo.class and the Test Java class for the same is SampleWCMUsePojoTest.class
    SampleWCMUsePojo pojoObj = new SampleWCMUsePojo();

Call init(Bindings bindings)
In actual code, this method gets called automatically when the POJO is instantiated via a data-sly-use call in HTL.
For Test class, we are calling this method explicitly after instantiating the POJO per the previous step
    pojoObj.init(mockBindings);

Call method under Test:
Given that we have instantiated the POJO under test and set dummy implementation for bindings per the above steps, now is the time to call the method under test.
    String actualTitle = pojoObj.getPageTitle();
    String expectedTitle = "Sample Title";
    assertEquals(expectedTitle, actualTitle);

Code Example :
Very Simple Use case :

If title and path are not authored in dialog, it has to return the currentPage title and path respectively. If it is authored, it has to return the same.
We need to consider/write tests for two possibilities within this
  • Getting title and path from dialog/component content resource node (component node under content where the component is authored)
    • Create mock resource JSON from component node under the content path. (type -> nt:unstructured)
    • Example: /content/learnings/en/wcmusepojo-unit-test-demo/jcr:content/root/responsivegrid/samplewcmusepojo.tidy.-1.json where samplewcmusepojo is the component name.
  • Getting title and path from the currentPage object.
    • Create mock page resource JSON from page node (type -> cq:Page)
    • Example: /content/learnings/en/wcmusepojo-unit-test-demo.tidy.-1.json
Full code of Simple WCMUsePojo Java class, Test class, and mock resource JSON is available on GitHub
In a similar fashion, we can extend the dummy implementation for binding of desired global objects and hence write test statements per the actual code logic.


By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.