April 7, 2020
Estimated Post Reading Time ~

JUnit in AEM

JUnit is a unit testing framework and is important in test-driven development. It is a piece of code, which executes another piece of code to validate if it results as expected.

Why JUnit?

  • It finds logical bugs early in the code, which makes our code more reliable.
  • JUnit is useful for developers, who work in a test-driven environment as it forces them to develop more readable, reliable and bug-free code which builds confidence during development.
  • It increases the productivity of the programmer and the stability of the program code, which in turn, reduces the stress on the programmer and the time spent on debugging.
Developers will pass dummy inputs to the code by mocking the necessary objects and validates if the code results as expected. However, developers must spend much time to mock sling resource objects to get good code coverage and most importantly they won’t be testing with the real data. Instead, they can test the code against the page content using wcm.io’s AEM Mocks. It creates a mock context that allows the APIs to mostly act as if they are running in AEM.
NOTE: context.loader is used to load page content in JSON format and it allows us to test the code against it. 

Now let us see how to write JUnit in AEM using SlingContext. Below is the sample sling model for which we’ll be writing JUnit.

Sample sling model:

import javax.inject.Inject;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.Optional;

@Model(adaptables = {Resource.class})
public class UserDetails {

@Inject @Optional
private String firstName;

@Inject @Optional
private String lastName;

public String getFirstName() {
return firstName;
}

public String getLastName() {
return lastName;
}
}

Getting started with JUnit:

Prerequisites: The following are the prerequisites before getting started.
  • Add the following dependencies to the POM.xml
<dependencies>
    …
    <!– Testing –>
     <dependency>
         <groupId>org.junit</groupId>
         <artifactId>junit-bom</artifactId>
         <version>5.4.1</version>
         <type>pom</type>
         <scope>import</scope>
     </dependency>
     <dependency>
         <groupId>org.slf4j</groupId>
         <artifactId>slf4j-simple</artifactId>
         <version>1.7.25</version>
         <scope>test</scope>
     </dependency>
     <dependency>
         <groupId>org.mockito</groupId>
         <artifactId>mockito-core</artifactId>
         <version>2.25.1</version>
         <scope>test</scope>
     </dependency>
     <dependency>
         <groupId>org.mockito</groupId>
         <artifactId>mockito-junit-jupiter</artifactId>
         <version>2.25.1</version>
         <scope>test</scope>
     </dependency>
     <dependency>
         <groupId>junit-addons</groupId>
         <artifactId>junit-addons</artifactId>
         <version>1.4</version>
         <scope>test</scope>
     </dependency>
     <dependency>
         <groupId>io.wcm</groupId>
         <artifactId>io.wcm.testing.aem-mock.junit5</artifactId>
         <version>2.4.4</version>
         <scope>test</scope>
     </dependency>
    …
</dependencies>
  • Copy-paste the page JSON (let us say, content.json) to src/main/resources folder.

How to get JSON data?

JSON data can be obtained by exporting page content using .infinity.json extension.
For example, let us consider that we are working on user details component in my-page ( http://localhost:4502/editor.html/content/we-retail/us/en/my-page.html ).

Hit http://localhost:4502/content/we-retail/us/en/my-page.infinity.json to get the JSON data which will be used further as an input to test the code.
We are good to get started now after performing the above steps. Below is the JUnit for the above sling model:

JUnit:

import static org.junit.Assert.assertEquals;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.testing.mock.sling.ResourceResolverType;
import org.apache.sling.testing.mock.sling.junit.SlingContext;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class UserDetailsTest {

            private UserDetails userDetails;
            private Resource resource;
            private final String myPagePath = “/content/we-retail/us/en/my-page”;

            @Rule
            public final SlingContext context = new SlingContext(ResourceResolverType.JCR_MOCK);

            @Before
            public void setUp() throws Exception {           
                        context.load().json(“/content.json”, myPagePath);
                        context.addModelsForPackage(“<mention-your-package-here>”);
            }

            @Test
            public void getFirstNameTest() throws Exception {
                        resource = context.resourceResolver()
                                                .getResource(myPagePath + “/jcr:content/root/responsivegrid/userdetails”);
                        userDetails = resource.adaptTo(UserDetails.class); 
                        assertEquals(userDetails.getFirstName(), “Lavanya”);
            }          

            @Test
            public void getLastNameTest() throws Exception {
                        resource = context.resourceResolver()
                                                .getResource(myPagePath + “/jcr:content/root/responsivegrid/userdetails”);
                        userDetails = resource.adaptTo(UserDetails.class); 
                        assertEquals(userDetails.getLastName(), “Malyala”);
            }

}
In the above JUnit,
  • addModelsForClasses registers the Sling Model to be tested, into the mock SlingContext, so it can be instantiated in the @Test methods.
  • load().json loads resource structures into the mock context, allowing the code to interact with these resources as if they were provided by a real repository. The resource definitions in the file content.json are loaded into the mock JCR context under /content/we-retail/us/en/my-page.
  • resourceResolver().getResource(myPagePath + “/jcr:content/root/responsivegrid/userdetails”) gets userdetails resource from JSON and asserts it respectively.
  • assertEquals asserts the expected value matches the value returned by the userdetails Sling Model object. If these values are not equal, the test will fail. Otherwise, it will pass.

How to get the code coverage?

Code coverage is the amount of source code covered by unit tests. Install the EclEmma plugin from the marketplace to get the code coverage.
Link for installation steps: EclEmma – Installation
Once the above plugin is installed, we can right-click on the Junit and Run As -> Junit Test.
We’ll see the above Junit as a success (in Junit tab), indicated by the green color status bar. Otherwise, it’ll be shown in red color. Also, we will get the code coverage details in Coverage tab as shown in the below screenshots:
JUnit status
JUnit coverage 
sourcehttps://aem.adobemarketingclub.com/junit-in-aem/


By aem4beginner

No comments:

Post a Comment

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