Unit Testing in AEM - Hands on

Unit Testing in AEM - Hands on

This post is about hands-on on Unit Testing Java class, part of an AEM application.
We will be using AEM Mocks from io.wcm.testing.mock.aem.junit5.*  and Mockito framework - org.mockito.* (reason for using this is explained in previous post)
  • JUnit Version : JUnit 5
  • AEM Maven archetype : 22
  • IDE : Eclipse
  • Testing related maven dependency : (available by default in AEM Maven archetype)
    • JUnit5 (org.junit)
    • AEM mocks (io.wcm.testing.aem-mock.junit5)
    • Mockito framework (org.mockito)
We mostly follow "Implementation first development" approach - Desired functionality is first coded and then test case is written for the same.

For creating Test Java class, 
  • Create Test Java Class
  • Create AemContext
  • Implement setUp method (creating test fixture, annotated with @BeforeEach)
  • Implement methods to be tested. (annotated with @Test)
Create Test Java Class:
In Eclipse IDE, right click on the Java class to be tested -> 
  • New -> Other -> JUnit -> JUnit Test case
We will have options to choose
  • JUnit version (as we have JUnit5 defined in project pom, JUnit Jupiter should be selected)
  • Displays the location of Test file (packages/file name)
  • Methods to be part of Test class file -  setUp method is selected by default where we define test fixture
  • Method to be considered for test - List of methods in our Java class to be tested will be available for selection.
  • After this initial selection, we have our test class file generated. (JUnit level annotations like @Test, @BeforeEach will be automatically added based on our selection in the process of creating Test Java class)
Create AemContext:
In order to initialize and make use of AEM context throughout all the test methods/set up method in a Test class file, 
  • We annotate the class using 
    • @ExtendWith(AemContextExtension.class)
  • Create instance variable for AemContext object as follows
    • private final AemContext aemContext = new AemContext();
Implement setUp()/@BeforeEach method:
As with name of annotation(@BeforeEach for setUp method), this is called before executing each test method. 
Note : For illustration, we will consider Sling Models - commonly used as part of AEM component developement
In our case, 
  • Class under test(Sling Model) to be registered/added to AemContext 
    • Below line will register class with @Model annotation. We have another method from AemContext to add models at package level - addModelsforPackage
      • aemContext.addModelsForClasses(CompUnderTestModel.class);
  • Mock resource definitions set up are to be done here. 
    • For models adapted as resource (where we act up on the resource in the repo), we are creating mock resource structure in the form of JSON to test the actions on the resource.
    • Below line will add the created mock resource structure to AemContext to act upon. 
      • private final String CONTENT_PATH = "/content/learnings/compundertestmodel";
      • private final String MOCK_RESC_JSON = "/learnings/core/models/CompUnderTestModel.json";
      • aemContext.load().json(MOCK_RESC_JSON, CONTENT_PATH);
      • MOCK_RESC_JSON is the json file created under class path - /src/test/resources (/src/test/resources/learnings/core/models/CompUnderTestModel.json)
      • CONTENT_PATH - Any meaningful dummy content path. 
    • Alternatively we have few other methods from AemContext for creating test content - create / build for creating Content Builder and Resource Builder objects respectively
Implement/Understanding @Test methods (behind the scene):
  • As part of mock resource definition in previous step, we loaded the json to dummy content path. Now the logic in test method would be to access that dummy content path and its loaded resource props to test against the expected. 
  • Example: 
    • Sling model is written to retrieve a title. 
    • Author the component in a page, fulfill the dialog values.
    • Get the JSON structure of authored resource (path of the component authored under specific page followed by .tidy.json)
    • Use that for creating mock resource JSON file. 
    • Lets say, the authored Title in the dialog to be "Sample Title". 
    • Then in the test method to test assertEquals(expected, actual), set the 
      • expected String -> Hardcode the value, same as the one authored in the component- "Sample Title"
        • String expectedString = "Sample Title";
      • actual String -> Retrieve the title from aemContext(which is loaded with mock resource structure at dummy content path)
        • Resource currentResc = aemContext.currentResource("/content/learnings/compundertestmodel");
        • ValueMap currentRescVal = currentResc.getValueMap();
        • String actualString = currentRescVal.get("title", String.class);
        • assertEquals(expectedString, actualString);
  • This distinction of actual and expected + each and every line of our actual code under test will help arrive at the desired test fixture and to perform other related tests like assertNotEquals/ assertTrue/ assertFalse and so on. In this case, setting up the aemContext correctly and hence implement test methods accordingly. 
Few sample snippets/test statements for reference:
Create Page :

pageManager() is available in the aemContext and hence we are using it to test the "create" method of the same.

Create Tag by Title:

Tag Manager is not available in aemContext and hence 

  • we annotate the class with MockitoExtension along with AemContextExtension which will be @ExtendWith({ AemContextExtension.class, MockitoExtension.class })
  • we mock the API using @Mock from org.mockito
  • provide dummy implementation via its "when" and "thenReturn" methods. 

Mockito throws an UnsupportedStubbingException, when an initialized mock is not used in test methods. To skip this exception/validation, we are using "lenient".

Full list of available methods from AemContext is available in the API doc
Complete Test Java class file for a Sling Model is explained with Video demo in WKND Tutorial

This is more of a starter to write Test Java class and we have many other options as part of Aem Mocks (from io.wcm) and several use cases like registering OSGI service, test class for servlets and so on which will be covered in upcoming posts.

Comments

  1. Thanks for delivering a good stuff, Explanation is good, Nice Article.
    software testing training in chennai

    ReplyDelete
  2. Really you have done a good job. Thanks for sharing this valuable information....
    Appium Training In Chennai
    Appium Training Online

    ReplyDelete
  3. The managed service provider takes on the 360-degree responsibility of the entire IT operations of the organisation, including 24X7 monitoring

    ReplyDelete
  4. Adroetech stands out as a premier company in the UAE, having received prestigious Microsoft awards for its excellence. They specialize in offering Dynamics 365 Business Central services and have extensive experience collaborating with Microsoft Dynamics Sales and more. When seeking top-notch Microsoft Dynamics 365 consulting providers in Dubai, Adroetech is the choice to make.

    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

Creation of Template Types for Editable templates