JUnit Test Cases for Sling Models based on Delegation Pattern | Community
Skip to main content
December 24, 2019
Solved

JUnit Test Cases for Sling Models based on Delegation Pattern

  • December 24, 2019
  • 12 replies
  • 28399 views

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 @2434638(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

Best answer by Rick_Holdsworth

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>

 

12 replies

chintan1997
January 18, 2022

Adding a better and cleaner solution for those who are having this issue. wcm.io provides libraries and extension for AEM applications. We can use the same to create AemContext. 

 

They have recently addresses this issue and added straightforward solution (issue link), details here. We can import 

com.adobe.cq.wcm.core.components.testing.mock.ContextPlugins.CORE_COMPONENTS and use them as a parameter to .plugin(). So our AemContext initialization would look like this.

 

private final AemContext ctx = new AemContextBuilder(ResourceResolverType.JCR_MOCK).plugin(CORE_COMPONENTS).build();

 

NOTE: ResourceResolverType.JCR_MOCK parameter is just for having extra features in my test case, that can be ignored too.

 

I used this plugin to implement test cases for custom list component that extends list component and uses delegate pattern for most of methods but uses some custom methods that uses delegated methods inside them. I didn't even have to adapt List model into AemContext. It didn't even require to mock anything for getting delegated patterns to work. 

JeevanRaj
Community Advisor
Community Advisor
April 6, 2023

Thanks for pointing me in the right direction. However I am still unable to make this work. None of the core components properties are being injected, I can only see the custom components properties. Below is my minimal unit test case. Would really appreciate any help here.

 

@ExtendWith(AemContextExtension.class)
public class FormTextModelTest {

private final AemContext context = new AemContextBuilder(ResourceResolverType.JCR_MOCK)
.plugin(CORE_COMPONENTS)
.build();

private FormTextModel formTextModel;

@BeforeEach
void setUp() {

context.create().resource("/apps/myproject/components/content/coreform/text",
"sling:resourceSuperType", "core/wcm/components/button/v2/button");

Page page = context.create().page("/content/test-page");

context.currentResource(context.create().resource(page, "extendedText",
"sling:resourceType", "myproject/components/content/coreform/text",
"required", "true",
"requiredMessage","Required message when required checkbox is true"
));

formTextModel = context.request().adaptTo(FormTextModel.class);
}

@Test
void testGetRequiredMessageWhenMsgIsAuthoredAndRequiredCheckBoxIsTrue() {
String expected = "Required message when required checkbox is true";
String actual = formTextModel.getRequiredMessage();
assertEquals(expected, actual);
}
}
sandeeprsahu11
Adobe Employee
Adobe Employee
October 26, 2023

The logic is simple, we need to get sling:resourceSuperType available at run time to insect Teaser in Sling model.

 

 

context.create().resource("/apps/project/components/core/teaser","sling:resourceSuperType", "core/wcm/components/teaser/v2/teaser");

 

 

@BeforeEach looks like below:

 

 

@BeforeEach public final void setUp() { context.create().resource("/apps/project/components/core/teaser","sling:resourceSuperType", "core/wcm/components/teaser/v2/teaser"); context.load().json("/teaser.json", "/content/site/teaser"); teaserComponent = Objects.requireNonNull(context.currentResource("/content/site/teaser")).adaptTo(TeaserComponent.class); }

 

 

 

 And Test looks like below:

 

 

//@Test void test() { assertNotNull(teaserComponent); assertNotNull(teaserComponent.getTeaser()); assertEquals("custom-field-value", teaserComponent.getCustomField()); }

 

 

TeaserComponentTest:

 

 

@ExtendWith(AemContextExtension.class) public class TeaserComponentTest { private final AemContext context = AppAemContext.newAemContext(); private TeaserComponent teaserComponent; /** * Setup method */ @BeforeEach public final void setUp() { context.create().resource("/apps/project/components/core/teaser","sling:resourceSuperType", "core/wcm/components/teaser/v2/teaser"); context.load().json("/teaser.json", "/content/site/teaser"); teaserComponent = Objects.requireNonNull(context.currentResource("/content/site/teaser")).adaptTo(TeaserComponent.class); } //@Test void test() { assertNotNull(teaserComponent); assertNotNull(teaserComponent.getTeaser()); assertEquals("custom-field-value", teaserComponent.getCustomField()); } }