I am trying to write jUnit test cases for custom teaser component which extends the core Teaser component using the Delegation Pattern for Sling Models using "@Self @Via(type=ResourceSuperType.class)" as specified here: https://github.com/adobe/aem-core-wcm-components/wiki/Delegation-Pattern-for-Sling-Models
When I try to set the context (io.wcm.testing.mock.aem.junit5.AemContext) and adapt the context's request (I've tried resource as well) to the model I have created (like the "PageHeadline" from the example), I am getting a NullPointerException.
I am using AEM 6.5.2.0 and trying to run test cases in JUnit 5
Solved! Go to Solution.
I've managed to find a solution.
Basically instead of relying on injection during the Unit test I've used Mockito and FieldSetter.setField
The only change to the source code would be to ensure the defaultInjectionStrategy was Optional. Not ideal but minimal
First we need to adapt the request to core Teaser.class
Teaser coreTeaser = request.adaptTo(Teaser.class);
Then we adapt the request to our custom teaser
CustomTeaser underTest = request.adaptTo(CustomTeaser.class);
Then we set the field that's not injecting
try {
FieldSetter.setField(underTest, underTest.getClass().getDeclaredField("teaser"), coreTeaser);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
Specific to Teaser I also had to set some SlingBindings so that @ScriptVariables were set when adapting the request to Teaser.class
@BeforeEach
request = context.request();
Component component = mock(Component.class);
Style style = mock(Style.class);
SlingBindings slingBindings = (SlingBindings) request.getAttribute(SlingBindings.class.getName());
slingBindings.put(WCMBindingsConstants.NAME_COMPONENT, component);
slingBindings.put(WCMBindingsConstants.NAME_CURRENT_STYLE, style);
request.setAttribute(SlingBindings.class.getName(), slingBindings);
Following may only be specific to our POM setup but thought I'd add for completeness
I also had to bump aem mock junit5 dependency to
<artifactId>io.wcm.testing.aem-mock.junit5</artifactId>
<version>4.0.0</version>
Hi,
You need to use one more adaptable to adapt Teaser Component
e.g.
@Model(adaptables = SlingHttpServletRequest.class, adapters = {Teaser.class, CustomTeaser.class}, resourceType = TeaserImpl.RESOURCE_TYPE)
Use CustomeTaeser to adapt to Junit test cases.
It will not cover 100% coverage but you will be able to write test cases for custom functionality.
Please check demo code:
https://github.com/arunpatidar02/aem63app-repo/blob/master/java/CustomTeaser.java
https://github.com/arunpatidar02/aem63app-repo/blob/master/java/CustomTeaserTest.java
There might be issue with init function inside your model class.
Views
Replies
Total Likes
Views
Replies
Total Likes
Views
Replies
Total Likes
Hi. I am having the same problem here. The core component property is always null in the custom implementation. And making it Optional just skips the instantiation of it.
Views
Replies
Total Likes
Views
Replies
Total Likes
Views
Replies
Total Likes
hi @arunpatidar - were you able to properties of Teaser in junit test class,
Views
Replies
Total Likes
I don't see the accepted solution as a solution. The model fails to adapt.
Making the field in the model optional hides the issue, but this too is not a solution (it just masks the problem).
Views
Replies
Total Likes
Hi Arun_PatidarEven I am facing a similar issue, can below is my component model
@Model(adaptables = SlingHttpServletRequest.class, adapters = {List.class, ComponentExporter.class, TeaserListModel.class}, resourceType = TeaserListModel.TEASER_LIST_RESOURCE_TYPE,defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class TeaserListModel implements List {
public static final String TEASER_LIST_RESOURCE_TYPE = "/components/template/teaserlist";
/*implementation*/
}
here is my Junit Test Case :
public abstract class AbstractTest {
public final void setUpTest() {
MockitoAnnotations.openMocks(this);
this.resolver = aemContext.resourceResolver();
this.request = aemContext.request();
this.response = aemContext.response();
this.pageManager = aemContext.pageManager();
String contentFilePath = this.getContentFilePath();
if (StringUtils.isNotEmpty(contentFilePath)) {
this.aemContext.load().json(contentFilePath, "/content");
}
}
class TeaserListModelTest extends AbstractTest {
@Override
protected String getContentFilePath() {
//loading the json here
return "/models/template/TeaserListModelTest.json";
}
@test
void testTeaserListModelValues() {
//aemContext is the context
Resource myResource = aemContext.resourceResolver().getResource("/TeaserList");
TeaserListModel model = myResource.adaptTo(TeaserListModel.class);
assertTrue(model.linkItems());
assertTrue(model.showDescription());
assertTrue(model.showModificationDate());
}
}
TeaserListModelTest.json file
{
"TeaserList": {
"jcr:primaryType": "nt:unstructured",
"showModificationDate": "true",
"jcr:createdBy": "admin",
"tagsMatch": "any",
"linkItems": "true",
"orderBy": "title",
"jcr:lastModifiedBy": "admin",
"jcr:created": "Mon Jul 12 2021 10:40:37 GMT-0400",
"sortOrder": "asc",
"id": "567",
"maxItems": "4",
"showDescription": "true",
"jcr:lastModified": "Mon Jul 12 2021 10:41:05 GMT-0400",
"childDepth": "4",
"listFrom": "children"
}
}
I am getting a NullPointer at TeaserListModel model = myResource.adaptTo(TeaserListModel.class);
can you check if resource exists?
Resource myResource = aemContext.resourceResolver().getResource("/TeaserList");
In json the resource type is missing as well.
Views
Replies
Total Likes
Views
Replies
Total Likes
have you registered the Sling model for the core teaser component as well to the context?
Jörg
Views
Replies
Total Likes
Thank you so much!
Views
Likes
Replies
Views
Likes
Replies