junit with AemContextExtension

srikanthg212933

03-05-2020

Hi I am trying to write junit using aem context for the first time, below is my sample sling model class.

 

@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class HeadlineModel {
private static final Logger LOGGER = LoggerFactory.getLogger(HeadlineModel.class);
@ValueMapValue
@Optional
private String size;
@ValueMapValue
@Optional
private String alignment;
@ValueMapValue
@Optional
private String headlinetext;
@ValueMapValue
@Optional
private String scheme;

public String getSize() {
return size;
}

public String getAlignment() {
return alignment;
}

public String getHeadlinetext() {
return headlinetext;
}

public String getScheme() {
return scheme;
}
}

Could someone give example to mock the above getters with latest way of using aemcontext

 

junit

Answers (1)

Answers (1)

Briankasingli

MVP

04-05-2020

@srikanthg212933,

Sure. There are really good resources online that should help you get started. Try here https://sourcedcode.com/category/aem/junit4

Original Blog: https://sourcedcode.com/aem-sling-models-unit-test-junit-4-with-examples

 

Header.class

package com.sourcedcode.core.models;

import com.day.cq.wcm.api.Page;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ChildResource;
import org.apache.sling.models.annotations.injectorspecific.ScriptVariable;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

import javax.annotation.PostConstruct;
import javax.inject.Inject;

@Model(adaptables = Resource.class,
        resourceType = Header.RESOURCE_TYPE,
        defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class Header {

    protected static final String RESOURCE_TYPE = "sourcedcode/components/structure/header";

    @ValueMapValue
    private String contactUsPath;

    @ScriptVariable(name="currentPage")
    private Page currentPage;

    @ChildResource(name="link")
    Resource childResource;

    @SlingObject
    private ResourceResolver resolver;

    private String contactUsPageSecretChar;

    @PostConstruct
    public void init() {
        setContactUsPageSecretChar();
    }

    private void setContactUsPageSecretChar() {
        Resource resource = resolver.getResource(contactUsPath);
        if (resource != null) {
            Page contactUsPage = resource.adaptTo(Page.class);
            contactUsPageSecretChar = contactUsPage.getTitle();
        }
    }

    public String getContactUsPageSecretChar() {
        return contactUsPageSecretChar.substring(contactUsPageSecretChar.length() - 1);
    }

    // demo of testing the @ScriptVariable("currentPage") annotation
    public String getPageTitle() {
        return currentPage.getPageTitle();
    }

    // demo of testing the @ChildResource annotation
    public String getChildLinkPropFlag() {
        return childResource.getValueMap().get("flag", "");
    }
}

HeaderTest.class

package com.sourcedcode.core.models;

import com.adobe.cq.commerce.common.ValueMapDecorator;
import com.day.cq.wcm.api.Page;
import com.google.common.collect.ImmutableMap;
import io.wcm.testing.mock.aem.junit.AemContext;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.testing.mock.sling.ResourceResolverType;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

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

@RunWith(MockitoJUnitRunner.class)
public class HeaderTest {

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

    // the context.resourceResolver() is auto injected by the AemContext, cannot be mocked.
    // ResourceResolver resolver;

    // mocking the global AEM object "currentPage".
    // variable does not need to match the variables in the underTest.class
    @Mock
    private Page currentPage;

    // injects all the mocks into the tested object.
    @InjectMocks
    private Header underTest;

    @Test
    public void itShouldReturnTheCorrectSecretCharWhenResourceExist() {
        // using the AEM context to create an AEM resource in the context, to set properties for the resource.
        // the resource path can be anything made up.
        Resource headerResourceContext = context.create().resource("/content/sourcedcode/home/jcr:content/header", new ValueMapDecorator(ImmutableMap.<String, Object> of(
                "contactUsPath", "/content/sourcedcode/contact",
                "anotherProperty", "example")));

        // create mock page, resolved by the resolver.
        context.create().page("/content/sourcedcode/contact", "", ImmutableMap.<String, Object>builder()
                .put("jcr:title", "Contact Us Page")
                .build());

        underTest = headerResourceContext.adaptTo(Header.class);
        assertEquals("e", underTest.getContactUsPageSecretChar());
    }

    @Test
    public void itShouldReturnTheCorrectCurrentPageTitle() {
        when(currentPage.getPageTitle()).thenReturn("Home Page");
        assertEquals("Home Page", underTest.getPageTitle());
    }

    @Test
    public void itShouldReturnTheCorrectChildLinkProperty() {
        context.build().resource("/content/sourcedcode/home/jcr:content/header")
                .siblingsMode()
                .resource("link", "flag", "newPage");
        underTest = context.resourceResolver().getResource("/content/sourcedcode/home/jcr:content/header").adaptTo(Header.class);
        assertEquals("newPage", underTest.getChildLinkPropFlag());
    }

}