Unit Testing WCMUse Classes using Mockito

05287_Unit-Testing-WCMUse-Classes-using-Mockito
Developers know testing code is important, and one of the best ways to do so is through unit tests. One of the questions preventing developers from writing unit tests, in Adobe Experience Manager development, is the question: how can I start writing tests, when required classes are only available in the context of a running AEM instance, that won’t fail because of a null pointer?

Recently I spent some time trying to answer that question. I found a simple solution, for a simple case that has promise to also cover some more complicated scenarios. The solutions involves using Mockito to mock objects and provide test return values for methods in classes not available outside the context of AEM.

Example WCMUse Class

 
package com.aempodcast.example.components;

import com.adobe.cq.sightly.WCMUse;
import org.apache.sling.api.resource.ValueMap;

public class Example extends WCMUse {

    private String title;
    private String type;
    
    @Override
    public void activate() {
        ValueMap properties = getProperties();
        title = properties.get("title", "");
        type = properties.get("type", "");
    }

    public String getTitle() {
        return title;
    }

    public String getType() {
        return type;
    }
}

Example Test Class

 
package com.aempodcast.example.components;

import java.util.HashMap;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.wrappers.ValueMapDecorator;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class ExampleTest {

    private Example example;
    private ValueMap properties;

    public ExampleTest() {
        // Create a mock of the WCMUse class.
        example = mock(Example.class);

        // Create test objects for mocking (properties ValueMap in this case).
        properties = new ValueMapDecorator(new HashMap());

        // Set what to use when superclass methods are called.
        when(example.getProperties()).thenReturn(properties);

        // Set to call the real methods in the class being tested. For methods that return something.
        when(example.getTitle()).thenCallRealMethod();
        when(example.getType()).thenCallRealMethod();

        // Set to call the real activate method in the class being tested. For void return types.
        doCallRealMethod().when(example).activate();
    }

    // Write tests
    @Test
    public void testGetTitle() {
        String testTitle = "test title";
        properties.put("title", testTitle);
        example.activate();
        String title = example.getTitle();
        assertEquals(testTitle, title);
    }

    @Test
    public void testGetType() {
        String testType = "test type";
        properties.put("type", testType);
        example.activate();
        String type = example.getType();
        assertEquals(testType, type);
    }

}

Conclusion
Writing tests for WCMUse classes, for the simple example, was easier than I expected. All that needs to be done to set up the test class for testing WCMUse classes is:

  • Create a mockup of the WCMUse class being tested.
  • Create objects used for testing (i.e., properties map).
  • Configure the mock object to do what is needed for the methods used.
    • Return a value needed for the test.
    • Call the actual method.
  • Write the tests.

Here are some references for mocked objects for testing code used in AEM: