Unit Testing in Adobe Experience Manager -part 1

In This post we will covers how to implement a Unit Test which validates the behavior of our recently created custom component called “AuthorModel” which is basically a Sling model class in AEM with some injection properties and use of multifield also covered in this Sling Model class. So our objective will be:


Objective

  • To Learn and Understanding of the basics of Junit testing.
  • To Learn and understanding about Junit5 frameworks and tools commonly used to test AEM code.
  • To Understanding the options for mocking or simulating AEM resources when writing unit tests cases for class or methods.

To work with Junit5 we need some dependencies to support the predefined JUNIT5 API’s.

  1. JUnit5
  2. Mockito Test Framework
  3. Apache Sling Mocks
  4. AEM Mocks Test Framework (io.wcm)

Dependencies Used:

            <!– https://mvnrepository.com/artifact/org.junit/junit-bom –>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.6.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!– https://mvnrepository.com/artifact/org.mockito/mockito-core –>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.3.3</version>
<scope>test</scope>
</dependency>
<!– https://mvnrepository.com/artifact/org.mockito/mockito-junit-jupiter –>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>3.3.3</version>
<scope>test</scope>
</dependency>

<!– https://mvnrepository.com/artifact/io.wcm/io.wcm.testing.aem-mock.junit5 –>
<dependency>
<groupId>io.wcm</groupId>
<artifactId>io.wcm.testing.aem-mock.junit5</artifactId>
<version>3.2.0</version>
<scope>test</scope>
</dependency>
<!– https://mvnrepository.com/artifact/junit-addons/junit-addons –>
<dependency>
<groupId>junit-addons</groupId>
<artifactId>junit-addons</artifactId>
<version>1.4</version>
<scope>test</scope>
</dependency>

Once the dependencies are added. In JUNIT5 we will use @ExtendWith(MockitoExtension.class) or @ExtendWith(AemContextExtension.class)



AEM Context JUnit Extension- AEM Mock Context is injected by using the @ExtendWith() annotation and simply passing the AemContextExtension.class inside the parameters of @ExtendWith() Which will take care about the context initialization and Cleanup thing required to run the test cases so every test case can run independently.

For Example:-

@ExtendWith({ AemContextExtension.class, MockitoExtension.class })

class AuthorModelTest {}

It is possible to combine such a unit test with a @ExtendWith annotation e.g. for Mockito JUnit Jupiter Extension.

In that case we have to define the AemContext field as a non-static field and we have to use @BeforeEach , @AfterEach annotations on methods.For each test, If you want to do setup to run each test case. And also we can use the static AemContext fields, using annotations as @BeforeAll and @AfterAll methods. In this case we have to make sure that there is no side-effects between the tests, as all changes in the AemContext object (e.g. content written to repository or OSGi services registered) are visible to all tests in the class. There are some Annotations which are changed in Junit5 from Junit4.In left side the annotation was used in junit4 and in right side annotations used in junit5.

@Before —–@BeforeEach

@After ——–@AfterEach

@BeforeClass ——– @BeforeAll

@AfterClass —-@AfterAll

@Ignore —-@Disabled


Setting up AEM test context

Whatever code we have write is totally relies on JCR for Data Transaction because data is being saved in JCR. And Junit test Cases are executed at build time So there is no data / Context is available at that time so io.WCM AEM MOCK make our life easy which provide the Context at build time and by using the AEM MOCK API’s we can get the data at Build time to validate the test cases.

@ExtendWith({ AemContextExtension.class, MockitoExtension.class })
class AuthorModelTest {
private final AemContext aemContext = new AemContext();
AuthorModel authorModel;

            @BeforeEach
void setUp() {
aemContext.addModelsForClasses(AuthorModel.class); aemContext.load().json(“/com/pstrainingsite/core/models/Author.json”,”/component”);

}


Loading the Json File using AEMContext:

In our class we just created an AEMContext using the wcm.io’s AemContextExtension.class and using the MockitoExtension.class as well for Mock. The Extwnsion will take care about all initialization required during the Build.

In the Void setUp(){} method, which will get  execute before the each @Test method, in which we are defining a common mock state.

The addModelsForClasses() will responsible for registering the Sling Model which we are going  to test, in the AEM Context mock, so it can be instantiated in the @Test methods.

The AemContext load().json method will  load the resource in the  Aem context Mock,  which will allow the code to interaction with the resources while build.

As they will be provided by a real repository. The resources is available in the file Author.json from /content CRXDE repo. Which looks like this.

{  “author”: {

             “jcr:primaryType”: “nt:unstructured”,

             “jcr:createdBy”: “admin“,

             “authorname“: “Sumit Kumar“,

             “gender”: “female”,

             “jcr:lastModifiedBy”: “admin“,

             “jcr:created”: “Wed Sep 08 2021 17:00:59 GMT+0530″,

             “link”: “/content/mytrainingsite/aem“,

             “text”: “<p>This created performance issues sometimes when my client library is too big and I try loading it in all p>\r\n”,

             “type”: “h3”,

             “nationality”: “true”,

The Author.json file is created inside the resources. Below is the code snippet for the entire AuthorModel.java Class

Here I will only show the getPagePath() method

public String getPagePath() {
return pagePath;
}

And below is the test case for the same

@Test
void getPagePath() {
aemContext.currentResource(“/component/author”);
authorModel = aemContext.request().adaptTo(AuthorModel.class);
String expected = “/content/pstrainingsite/custom-template-Page”;
Page page = aemContext.create().page(“/content/pstrainingsite/custom-template-Page”);
String actual = page.getPath();
assertEquals(expected, actual);
assertNotEquals(“unexpected”, authorModel.getPagePath());
assertNotSame(“”, authorModel.getPagePath());
assertNotEquals(“”, authorModel.getPagePath());
assertSame(authorModel, authorModel);

}




In next part of this Junit post we will see the entire code for Sling model class and test class.

If you really find this post useful, you can help me to keep this alive by buying me a cup of Coffee.

Stay Safe & Healthy                                              Happy Learning & Coding