Saturday, March 21, 2020

Unit testing or Junit for Sling models using Mockito in AEM


Unit testing or Junit for Sling models using Mockito in AEM


  • First generate AEM sample project with the help of below archetype which will download the sample AEM project compatible in AEM 6.5



mvn -B archetype:generate -D archetypeGroupId=com.adobe.granite.archetypes -D archetypeArtifactId=aem-project-archetype -D archetypeVersion=23 -D aemVersion=cloud -D appTitle="Training" -D appId="training" -D groupId="com.training"-D frontendModule=general -D includeExamples=n

           This will generate a project with below structure





Now add the dependency for Mockito

  • Add below entry in main pom.xml

           
    

  • Add below entry to core/pom.xml .       


     

  • Create new Sling model class with name 'HelloIndiaModel' which will have Sling Objects such as Resource and ResourceResolver, init method and public method getMessage which will return a message based on few conditions.

import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageManager;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.SlingObject;

import javax.annotation.PostConstruct;
import java.util.Optional;

@Model(adaptables = Resource.class)
public class HelloIndiaModel {

    @SlingObject
    private Resource currentResource;

    @SlingObject
    private ResourceResolver resourceResolver;

    private String message;

    @PostConstruct
    protected void init() {
        PageManager pageManager = resourceResolver.adaptTo(PageManager.class);
        String currentPagePath = Optional.ofNullable(pageManager)
                .map(pm -> pm.getContainingPage(currentResource))
                .map(Page::getPath).orElse("");

        message = "Hello " + getRegion(currentPagePath);
    }

    private String getRegion(String currentPagePath){
        if(currentPagePath.contains("geometrixx")){
            return "Geometrixx";
        }else if(currentPagePath.contains("India")){
            return "Indians";
        }
        else {
            return "World";
        }
    }

    public String getMessage() {
        return message;
    }

}

  • Now go to test folder , create test class for HelloIndiaModel, this class should have an annotation @RunWith(MockitoJUnitRunner.class) which keeps tests clean and improves debugging experience.

  • Try to understand the logic of init method from HelloIndiaModel, first line is using resourceresolver to adapt to PageManager. Since both of these objects are AEM/Sling Objects so we need to mock these Objects in our HelloIndiaModelTest class

  • Line 3 is trying to get the Page from page manager object, Page Object is also belongs to AEM hence we need to mock this object as well

    @Mock
    private Resource currentResource;

    @Mock
    private ResourceResolver resourceResolver;

    @Mock
    private Page currentPage;

    @Mock
    private PageManager pageManager;



  • We have used the @Mock since we need these objects to support testing of class to be tested.
  • Now we need to call the Object of the class which we wish to write test, which can be done by . using the @InjectMocks

    @InjectMocks
    private HelloIndiaModel helloIndiaModel; 

  • Now we need to handle the logic which is written using sling objects in sling model. For example :- what should mockito do when it reaches to the line resourceResolver.adaptTo(PageManager.class); We can specify this with the help of @Before method. 



Methods annotated with the @Before annotation are executed before each test. This is useful when we want to execute some common code before running a test.

@Before
public void setUp() {

    when(resourceResolver.adaptTo(PageManager.class)).thenReturn(pageManager);
    when(pageManager.getContainingPage(currentResource)).thenReturn(currentPage);
    when(currentPage.getPath()).thenReturn("/content/geometrixx");

    helloIndiaModel.init();

}

  • Write first test for the method getMessage

   @Test
   public void shouldMatch(){
         assertNotNull(helloIndiaModel.getMessage());
         assertEquals("Hello Geometrixx",helloIndiaModel.getMessage());
   }


Please find below the complete Test class

import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageManager;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import static junit.framework.Assert.assertNotNull;
import static junitx.framework.Assert.assertEquals;
import static junitx.framework.Assert.assertNotEquals;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class HelloIndiaModelTest {

    @Mock
    private Resource currentResource;

    @Mock
    private ResourceResolver resourceResolver;

    @Mock
    private Page currentPage;

    @Mock
    private PageManager pageManager;

    @InjectMocks
    private HelloIndiaModel helloIndiaModel;

    //initialize
    @Before
    public void setUp() {

        when(resourceResolver.adaptTo(PageManager.class)).thenReturn(pageManager);
        when(pageManager.getContainingPage(currentResource)).thenReturn(currentPage);
        when(currentPage.getPath()).thenReturn("/content/geometrixx");

        helloIndiaModel.init();

    }

    @Test
    public void shouldNotMatch(){
        assertNotNull(helloIndiaModel.getMessage());
        assertNotEquals("Hiiiii",helloIndiaModel.getMessage());
    }

    @Test
    public void shouldMatch(){
        assertNotNull(helloIndiaModel.getMessage());
        assertEquals("Hello Geometrixx",helloIndiaModel.getMessage());
    }


1 comment: