I extended the Text Core Component's Java Class with a simple "moreText" variable.
But I am still using some of the Core Component's methods in the HTL so I need them in my Java Class.
How can I Unit Test the Extended Core Component's methods that require the ResourceSuperType?
package com.adobe.aem.guides.wknd.core.models;
import com.adobe.cq.export.json.ComponentExporter;
import com.adobe.cq.export.json.ExporterConstants;
import com.adobe.cq.wcm.core.components.models.Text;
import lombok.Getter;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.models.annotations.Default;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.Via;
import org.apache.sling.models.annotations.injectorspecific.Self;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
import org.apache.sling.models.annotations.via.ResourceSuperType;
@Model(
adaptables = SlingHttpServletRequest.class,
adapters = {ComponentExporter.class},
resourceType = ExtendedCoreComponentModel.RESOURCE_TYPE,
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
@Exporter(
name = ExporterConstants.SLING_MODEL_EXPORTER_NAME,
extensions = ExporterConstants.SLING_MODEL_EXTENSION
)
public class ExtendedCoreComponentModel implements Text {
static final String RESOURCE_TYPE = "wknd/components/extendedcorecomponent";
@Self
@Via(type = ResourceSuperType.class)
protected Text text;
@getter
@ValueMapValue
@default(values = "Hello World.")
private String moreText;
@Override
public String getText() {
return text.getText();
}
@Override
public boolean isRichText() {
return text.isRichText();
}
}
Views
Replies
Total Likes
@jeremylanssiers Please check out this post on how you can do null check for
https://experienceleaguecommunities.adobe.com/t5/adobe-experience-manager/junit-test-cases-for-sling...
Also the example here
https://github.com/arunpatidar02/aem63app-repo/blob/master/java/CustomTeaser.java
https://github.com/arunpatidar02/aem63app-repo/blob/master/java/CustomTeaserTest.java
hope this helps!
Thanks for the help but this is not a solution. I saw these answers you mention before posting this question.
The first Q&A uses ...
FieldSetter.setField()
... but this is a deprecated method and field.
The second Q&A literally mentions they cannot test overridden methods.
The code you share in your repo also avoids unit testing an overridden method (or a ResourceSuperType).
Hello @jeremylanssiers ,
You can follow this article where each step is described: https://medium.com/@sadyrifat/aem-unit-test-case-for-sling-delegation-pattern-1f011b947fb3
Another GitHub commit link and repo for check your maven dependency also: https://github.com/Sady-Rifat/aem-demo/commit/81ce47fc28b70d31bee0034e2cadf69b721d892e
Hope this will help you.
I have not been able to test your solution yet. However I was able to test a ResourceSuperType by directly injecting a mock value. E.g.
@Mock
CoreComponent coreComponent;
ExtendedCoreComponent extendedCoreComponent = new ExtendedCoreComponent();
extendedCoreComponent.coreComponent =coreComponent;
The obvious drawback is I had to make that coreComponent protected instead of private in my ExtendedCoreComponent class.
I was able to find a solution looking at the Unit test for WKND project's ImageList at
https://github.com/adobe/aem-guides-wknd/blob/main/core/src/test/java/com/adobe/aem/guides/wknd/core...
The magic happens at these lines:
Field reader = ExtendedCoreComponentImpl.class.getDeclaredField("text");
reader.setAccessible(true);
reader.set(extendedCoreComponentImpl, text);
Class
package com.adobe.aem.guides.wknd.core.models.impl;
import com.adobe.aem.guides.wknd.core.models.ExtendedCoreComponent;
import com.adobe.cq.export.json.ComponentExporter;
import com.adobe.cq.export.json.ExporterConstants;
import com.adobe.cq.wcm.core.components.models.Text;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.models.annotations.Default;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.Via;
import org.apache.sling.models.annotations.injectorspecific.Self;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
import org.apache.sling.models.annotations.via.ResourceSuperType;
@Model(
adaptables = SlingHttpServletRequest.class,
adapters = { ExtendedCoreComponent.class,ComponentExporter.class},
resourceType = ExtendedCoreComponentImpl.RESOURCE_TYPE,
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION)
public class ExtendedCoreComponentImpl implements ExtendedCoreComponent {
static final String RESOURCE_TYPE = "wknd/components/extendedcorecomponent";
@Self
(type = ResourceSuperType.class)
private Text text;
@ValueMapValue
(values="Hello World.")
private String moreText;
public String getMoreText() {
return moreText;
};
public String getText() {
return text.getText();
}
public boolean isRichText() {
return text.isRichText();
}
public String getExportedType() {
return ExtendedCoreComponentImpl.RESOURCE_TYPE;
}
}
Mock data
{
"jcr:primaryType": "nt:unstructured",
"moreText": "moreText",
"id": "id",
"text": "<p>text</p>",
"sling:resourceType": "wknd/components/extendedcorecomponent",
"textIsRich": "true"
}
Unit Test
package com.adobe.aem.guides.wknd.core.models.impl;
import com.adobe.aem.guides.wknd.core.utils.TestUtil;
import com.adobe.cq.wcm.core.components.models.Text;
import io.wcm.testing.mock.aem.junit5.AemContext;
import io.wcm.testing.mock.aem.junit5.AemContextExtension;
import org.apache.sling.testing.mock.sling.ResourceResolverType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.lang.reflect.Field;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.when;
@ExtendWith({AemContextExtension.class, MockitoExtension.class})
class ExtendedCoreComponentImplTest {
private final AemContext aemContext = new AemContext(ResourceResolverType.JCR_MOCK);
private static final String JCR_PATH = TestUtil.getMockJcrPath("extendedCoreComponent");
private static final String MOCK_DATA = "/components/extendedCoreComponent/extendedCoreComponent.json";
private static final String MOCK_DATA_EMPTY = "/components/extendedCoreComponent/extendedCoreComponent-empty.json";
@Mock
private Text text;
@InjectMocks
private ExtendedCoreComponentImpl extendedCoreComponentImpl;
@BeforeEach
void setup() {
aemContext.addModelsForPackage(TestUtil.CORE_PACKAGES);
}
@Nested
class ExtendedComponent {
@BeforeEach
void setUp() {
aemContext.load().json("/pages/pages.json", "/content");
}
void emptyComponent() {
extendedCoreComponentImpl = TestUtil.loadSlingModelViaSlingHttpServletRequest(aemContext, ExtendedCoreComponentImpl.class, MOCK_DATA_EMPTY, JCR_PATH);
assertEquals("Hello World.", extendedCoreComponentImpl.getMoreText());
}
void component() {
extendedCoreComponentImpl = TestUtil.loadSlingModelViaSlingHttpServletRequest(aemContext, ExtendedCoreComponentImpl.class, MOCK_DATA, JCR_PATH);
assertEquals("moreText", extendedCoreComponentImpl.getMoreText());
}
}
@Nested
class CoreComponent {
@BeforeEach
void setup() {
aemContext.load().json(MOCK_DATA, "/content");
aemContext.addModelsForClasses(Text.class);
}
void coreComponent() throws NoSuchFieldException, IllegalAccessException {
extendedCoreComponentImpl = TestUtil.loadSlingModelViaSlingHttpServletRequest(aemContext, ExtendedCoreComponentImpl.class, MOCK_DATA, JCR_PATH);
Field reader = ExtendedCoreComponentImpl.class.getDeclaredField("text");
reader.setAccessible(true);
reader.set(extendedCoreComponentImpl, text);
when(text.isRichText()).thenReturn(true);
when(text.getText()).thenReturn("abc");
assertTrue(extendedCoreComponentImpl.isRichText());
assertEquals("abc", extendedCoreComponentImpl.getText());
}
}
}
Test Util Class
package com.adobe.aem.guides.wknd.core.utils;
import io.wcm.testing.mock.aem.junit5.AemContext;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.factory.ModelFactory;
import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletRequest;
public class TestUtil {
public static final String CORE_PACKAGES = "com.adobe.aem.guides.wknd.core";
private static final String JCR_PAGE_PATH = "/content/project/country/language/page/jcr:content/root/container/container/%s";
public static String getMockJcrPath(String componentName) {
return String.format(JCR_PAGE_PATH, componentName);
}
public static <T> T loadSlingModel(AemContext aemContext, Class<T> clazz, String mockDataPath, String mockJcrPath) {
boolean isAdaptableFromResource = false;
boolean isAdaptableFromSlingHttpServletRequest = false;
Class<?>[] adaptables = clazz.getAnnotation(Model.class).adaptables();
for (Class<?> adaptable : adaptables) {
if (StringUtils.equals(adaptable.getName(), Resource.class.getName())) {
isAdaptableFromResource = true;
}
if (StringUtils.equals(adaptable.getName(), SlingHttpServletRequest.class.getName())) {
isAdaptableFromSlingHttpServletRequest = true;
}
}
if (isAdaptableFromResource) {
return loadSlingModelViaResource(aemContext, clazz, mockDataPath, mockJcrPath);
}
if (isAdaptableFromSlingHttpServletRequest) {
return loadSlingModelViaSlingHttpServletRequest(aemContext, clazz, mockDataPath, mockJcrPath);
}
return null;
}
public static <T> T loadSlingModelViaSlingHttpServletRequest(AemContext aemContext, Class<T> clazz, String mockDataPath, String mockJcrPath) {
aemContext.load().json(mockDataPath, mockJcrPath);
Resource resource = aemContext.resourceResolver().getResource(mockJcrPath);
MockSlingHttpServletRequest request = aemContext.request();
request.setResource(resource);
ModelFactory modelFactory = aemContext.getService(ModelFactory.class);
return modelFactory.createModel(request, clazz);
}
public static <T> T loadSlingModelViaResource(AemContext aemContext, Class<T> clazz, String mockDataPath, String mockJcrPath) {
aemContext.load().json(mockDataPath, mockJcrPath);
Resource resource = aemContext.resourceResolver().getResource(mockJcrPath);
ModelFactory modelFactory = aemContext.getService(ModelFactory.class);
return modelFactory.createModel(resource, clazz);
}
}
Hello @jeremylanssiers ,
Thanks for sharing the solution.
Views
Replies
Total Likes
Views
Likes
Replies
Views
Likes
Replies