Unit Testing Hands on - For WCMUsePojo

Unit Testing for WCMUsePojo

This post illustrate 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 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 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 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 data-sly-use call in HTL.
For Test class, we are calling this method explicitly after instantiating the POJO per 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 is not authored in dialog, it has to return 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 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 in 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.

Comments

  1. Unit testing is one of the software testing types which includes the initial testing phase where the smallest components or the modules of a software are tested individually. With this method of testing, both testers and developers can isolate each module, identify and fix the system defects at a very early stage of the software development lifecycle (SDLC). Primarily, a unit test verifies different behavioral aspects of the system under test and can be broadly classified into state-based and interaction-based unit testing.

    ReplyDelete

Post a Comment

Popular posts from this blog

Embedding Third party dependency/OSGi bundle in AEM application hosted in AEMasCS

OSGI Factory Configuration implementation

Embed Third party dependency using bnd-maven-plugin