Tuesday 13 October 2020

Writing Junit test cases

 

Basics:

Java file packages are present in /src/main/java folder, so same package name should be present in /src/test/java with same file name just append “Test” in last

Example: Sample.java

For this class Test class name should be SampleTest.java

 

Installation Instructions:

Step1) Go here https://www.eclipse.org/downloads/ and click on "Download 64 bit" button, this will download 102 MB eclipse Installer .exe file

Step 2) Run this Installer .exe it will download eclipse 2020-09 [Latest]

Step 3) Open Eclipse ->Project -> Properties->Java Build Path -> Add Library

Select Junit and click next, it will populate Junit 5 in Dropdown just click on finish

Step4) Now run coverage it works fine

 Dependencies:

Add below dependencies in pom to include Junit 4 & 5 

Download Dependencies

Points to be noted:

 Junit can be written using different versions:

A) Junit 4 - This provides options to use PowerMock which can be use to mock Static class methods without going inside it. This feature is not available in Junit 5. [Refer point 17 for example.]

B) Junit 5 - This can be used for almost all clases as its the latest one but in case we stuck with Static class methods we can use Junit 4.

    Junit 5 Test case can be written in two ways

    a) Normal way where we can mock each object.

    b) AemContext JSON way where all properties are configured in JSON [This works fine for models where we just need to read resource properties, for complex business logic use first approach.] 


1)  For Junit 5: On top of test class name add below annotation
            
            @ExtendWith(AemContextExtension. class)

// This should be used when context object needs to be used

Refer below document for more details

https://docs.adobe.com/content/help/en/experience-manager-learn/getting-started-wknd-tutorial-develop/unit-testing.html

 

 

2)      For Junit 4: On top of test class name add below annotation


 @RunWith(PowerMockRunner.class)

// This can be used for line coverage

 3)      Define below methods in every Junits

                         @Before

                public void setUp() throws Exception {

                          // declare all mock object here

                          //set all private variable here using PrivateAccessor

                          // Try to handle all possible logic line here why adding when().thenReturn() to define outputs of it as we want

                          //create global object of .java class and use it to call all related method of .java class

                          // to cover all lines coverage

                }

                @After

                public void tearDown() throws Exception {

                          // If any new global object created for any class here, make them null here.

                }


                Now for all the public methods defined in .java class Test method needs to be created with below format

If one of the method name in .java class is getSitemapIndex then create below test method

     @Test

                public final void testGetSitemapIndex() {

                          // use any testing method here to compare any variable output from that method

                          // Example: assertEquals(StringUtils.EMPTY,helperObj.getSitemapIndex());

                }

 4)      If we have used below variables anywhere in class, we can get it from either aem context object

Or we can mock it if using power mock

Variables: slingRequest, slingResponse, session, resolver, pageManager

  

When using AEM Context:

    private final AemContext context = new AemContext();

    private final MockSlingHttpServletRequest request = context.request();

    private final MockSlingHttpServletResponse response = context.response();

    private final Session session = context.resourceResolver().adaptTo(Session.class);

    private final ResourceResolver resResolver = context.resourceResolver();

    private final PageManager pageManager = context.pageManager();

 

  • 5)      We can mock all objects which ever is used in the .java calls

Few examples:

Resource res = mock(Resource.class);

ValueMap pageProperties = mock(ValueMap.class);

 

  • 6)      If any method is called on any object, then it can throw Null Pointer Exception, as it can be null.

So, to handle this scenario, we can set that class level variables or static objects from Junit itself to avoid Null pointer.

Example:

If we are using resource.getPath(), here it’s giving Null pointer Exception, we should add below line to avoid it

Resource res = mock(Resource.class);

PrivateAccessor.setField(introductionCopyHelper, "res", res);

 

Note: This is used to set Class level variables or static objects. For local vars, values are often set as a return value of a method which can be handled by “when” and “thenReturn”. But can’t do the same for static objects.

 

  • 7)      We can use below methods to test code

 

import static org.junit.Assert.assertEquals;

import static org.junit.jupiter.api.Assertions.assertNotNull;

import static org.mockito.Mockito.mock;

import static org.mockito.Mockito.when;

import org.junit.After;

import org.junit.Before;

import org.junit.Test;

import io.wcm.testing.mock.aem.junit5.AemContextExtension;

import org.powermock.core.classloader.annotations.PrepareForTest;

import junitx.util.PrivateAccessor;


 

Page mockPage = mock(Page.class);

when(mockPage.getPath()).thenReturn("/content/www/global/en");

assertEquals("my-static-string", someHelperObj.getMyComponentId());

 

There are lot more methods available like assertNull, assertNotNull, assertFalse etc.

You can find all list of available methods in below API reference document: https://junit.org/junit5/docs/5.0.1/api/org/junit/jupiter/api/Assertions.html

 

  • 8)  To set field of any super class, use below syntax

 Example: Here its setting currentPage field value with mocked page object.

import java.lang.reflect.Field;

 Field privateOrProtectedField = contentHelper.getClass()

.getSuperclass().getDeclaredField("currentPage");

privateOrProtectedField.setAccessible(true);

privateOrProtectedField.set(contentHelper, mockPage);


9) Handle Static classes

Example: we need to test StringUtils.isNotBlank for below lines of code:

                String pageSelectors = slingRequest.getRequestPathInfo().getSelectorString();

                if (StringUtils.isNotBlank(pageSelectors)) {

Then handle it like shown below:

                String pageSelectors = "Test Page Selector";

        when(slingRequest.getRequestPathInfo().getSelectorString()).thenReturn(pageSelectors);

                assertTrue(StringUtils.isNotBlank(pageSelectors));


Also, to handle Final class like Locale whose object cannot be created, used below syntax to handle it

import org.apache.commons.lang.LocaleUtils;

  Locale locale = LocaleUtils.toLocale("us");


10) If some property check is not working, Debug Junit and check if its coming correct or not.

Sometimes if instance of object is different it does not work or comes as null.

Example:

String templateType = Util.getStringProperty(currentPageProperties, Constants.TEMPLATE_PROP_KEY);        

if (StringUtils.isNotBlank(templateType)) {

In this case debug and check template type should not come as null

when(currentPage.getProperties()).thenReturn(currentPageProperties);

assertNotNull(currentPageProperties);        when(currentPageProperties.containsKey(Constants.TEMPLATE_PROP_KEY)).thenReturn(true);

 when(currentPageProperties.get(Constants.TEMPLATE_PROP_KEY)).thenReturn("Test Template");

 when(Util.getStringProperty(currentPageProperties, Constants.TEMPLATE_PROP_KEY))

                .thenReturn(templateType);

        assertTrue(StringUtils.isNotBlank(templateType));


11) When we want to mock with generic params, whose value can be anything

Eg: 

import static org.mockito.ArgumentMatchers.anyMap;

import static org.mockito.ArgumentMatchers.anyString;

when(contComp.prepareQueryParams(anyString(), anyMap())).thenReturn(mockParams);


12) For handling new objects

PowerMockito.whenNew(HierarchyNodeInheritanceValueMap.class).withArguments(mockResource).thenReturn(mockCurrentPageInherited);

13) Scenario where we need to iterate in a loop

    For Example we have Java class with below block:

                Iterator<Resource> allChildren = resource.getChildren().iterator();

while (allChildren.hasNext()) {

Resource childResource= allChildren.next();

                }

Then for this JUnit should be like mentioned below [this will iterate 2 times]

        Iterable<Resource> mockIterable = (Iterable<Resource>) mock(Iterable.class);

        when(res.getChildren()).thenReturn(mockIterable);

        Iterator<Resource> mockIterator = (Iterator<Resource>) mock(Iterator.class);

        when(mockIterable.iterator()).thenReturn(mockIterator);

        when(mockIterator.hasNext()).thenReturn(true).thenReturn(true).thenReturn(false);

        when(mockIterator.next()).thenReturn(res);


14) In case catch block also needs to be covered, this is how exception can be thrown from Junit

when(mockResourceResolver.getResource(mockCurrentPage.getPath())).thenThrow(NullPointerException.class);

For void return type methods throw exception as mentioned below:

        import static org.mockito.Mockito.doThrow;

        doThrow(new AccessDeniedException()).when(basicSession).save();

15) Mocking parameterized constructor

import static org.powermock.api.mockito.PowerMockito.whenNew;

Second second = Mockito.mock(Second.class);

whenNew(Second.class).withNoArguments().thenReturn(second);


16) Call private method

import org.mockito.MockitoAnnotations;

import java.lang.reflect.Method;


MockitoAnnotations.initMocks(this);

Method postConstruct = MyClass.class.getDeclaredMethod("initModel", null); // methodName,parameters

postConstruct.setAccessible(true);

postConstruct.invoke(myclassObj);

.............................

Also in case its a private constructor then use below code

final Constructor<?>[] constructors = MyClass.class.getDeclaredConstructors();

        for (final Constructor<?> constructor : constructors) {

            Assert.assertTrue(Modifier.isPrivate(constructor.getModifiers()));

        }

        constructors[0].setAccessible(true);

// In this case private contructor was parameterized and was having two strings as params

        myClassObj= (MyClass) constructors[0].newInstance(TEST_DATA, TEST_DATA);


17) Mock Static class

Put these annotation on top of class name

@RunWith(PowerMockRunner.class)

@PrepareForTest({ MyUtil.class})


 Use below lines in method

PowerMockito.mockStatic(MyUtil.class);

when(MyUtil.getURL(redirectPath)).thenReturn("http://www.google.com");


18) If getting any exception in Gson, link any Gson internal class not found then include below dependency in pom to resolve this issue

<dependency>  

         <groupId>com.google.code.gson</groupId>

<artifactId>gson</artifactId>

<version>2.8.0</version>

</dependency>


19) How to handle SlingQuery $() statements

Example to handle below syntax

SlingQuery slingQuery = $(currentResource).closest("somestring");


Use below Junit code

import org.apache.sling.query.SlingQuery;

@RunWith(PowerMockRunner.class)

@PrepareForTest({ SlingQuery.class })

private SlingQuery slingQuery;

slingQuery = mock(SlingQuery.class);

when(slingQuery.toString()).thenReturn("test-data");

PowerMockito.mockStatic(SlingQuery.class);

when(SlingQuery.$(resource)).thenReturn(slingQuery);

when(slingQuery.closest("test-data")).thenReturn(slingQuery);



20) How to mock super class method which is not visible
import org.powermock.api.support.membermodification.MemberMatcher;

PowerMockito.suppress(MemberMatcher.methodsDeclaredIn(MyParentClass.class));
Note: This will work only when the superclass method does not return anything.

No comments:

Post a Comment