In Unit Test, @ChildResource annotation property always return null. Can anyone please guide me to resolve the issue?
Environment Details: AEM 6.5.16 and Java 11
CountryListImpl.java
@Model(
adaptables = SlingHttpServletRequest.class,
adapters = { CountryList.class },
resourceType = CountryListImpl.RESOURCE_TYPE,
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
@Exporter(
name = ExporterConstants.SLING_MODEL_EXPORTER_NAME,
extensions = ExporterConstants.SLING_MODEL_EXTENSION
)
public class CountryListImpl implements CountryList {
protected static final String RESOURCE_TYPE = "aem-demo/components/country-list";
protected static final String COUNTRY_LANGUAGE_LIST = "countryItems";
@ValueMapValue
@Named("title")
String defaultLabel;
@ChildResource(name = COUNTRY_LANGUAGE_LIST)
List< CountryItem > countryList;
public String getDefaultLabel() {
return defaultLabel;
}
public List<CountryItem> getCountryList() {
return countryList;
}
}
CountryItemImpl.java
@Model(
adaptables = Resource.class,
adapters = { CountryItem.class },
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
public class CountryItemImpl implements CountryItem {
@ValueMapValue
String country;
@ValueMapValue
String language;
@ValueMapValue
String link;
public String getCountry() {
return country;
}
public String getLanguage() {
return language;
}
public String getLink() {
return link;
}
}
CountryListImplTest.java
@ExtendWith(AemContextExtension.class)
public class CountryListImplTest {
private final AemContext ctx = new AemContext(ResourceResolverType.JCR_MOCK);
private CountryList countryList;
@BeforeEach
public void setup() {
ctx.load().json(
"/com/aem/demo/core/components/internal/models/v1/country-list.json",
"/content"
);
ctx.currentResource("/content/country-list");
countryList = Objects.requireNonNull(ctx.getService(ModelFactory.class))
.createModel(ctx.request(), CountryListImpl.class);
}
public void getCountryListTest() {
Assertions.assertNotNull(countryList);
// countryList.getCountryList() --> ALWAYS RETURN NULL
// Assertions.assertNotNull(countryList.getCountryList());
// Assertions.assertEquals(
// 2, countryList.getCountryList().size()
// );
}
}
country-list.json
{
"country-list": {
"jcr:primaryType": "nt:unstructured",
"sling:resourceType": "awcm-demo/components/country-list",
"title": "Select ...",
"countryItems": {
"jcr:primaryType": "nt:unstructured",
"item0": {
"jcr:primaryType": "nt:unstructured",
"country": "Austria",
"link": "/content/aem-demo/at/de",
"language": "German"
},
"item1": {
"jcr:primaryType": "nt:unstructured",
"country": "Canada",
"link": "/content/aem-demo/ca/en",
"language": "English"
}
}
}
}
Solved! Go to Solution.
Views
Replies
Total Likes
I've identified a simplified solution, and I only need to implement two modifications.
1. CountryListImpl: change adaptables to Resource.Class
@Model(
adaptables = Resource.class,
adapters = { CountryLinkButton.class },
resourceType = CountryLinkButtonImpl.RESOURCE_TYPE,
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
@Exporter(
name = ExporterConstants.SLING_MODEL_EXPORTER_NAME,
extensions = ExporterConstants.SLING_MODEL_EXTENSION
)
public class CountryLinkButtonImpl implements CountryLinkButton {
.....
}
2. CountryListImplTest: Update createModel
createModel(ctx.request().getResource(), CountryListImpl.class);
Reference: https://medium.com/@veena.vikraman19/aem-junit-for-simple-aem-component-with-multifield-2326f61c8982
Views
Replies
Total Likes
Hi,
In the given test case, the @ChildResource annotation may return null because the AEM context used for testing is not properly initialized to include the child resources from the JSON file. To resolve this issue, you can modify the test setup method as follows:
@BeforeEach
public void setup() {
ctx.load().json(
"/com/aem/demo/core/components/internal/models/v1/country-list.json",
"/content"
);
// Update the path to include the child resource
ctx.currentResource("/content/country-list/countryItems");
countryList = Objects.requireNonNull(ctx.getService(ModelFactory.class))
.createModel(ctx.request(), CountryListImpl.class);
}
-> By updating the path to include /countryItems, you ensure that the child resource is loaded correctly, and the @ChildResource annotation can populate the countryList property.
Please note that the JSON file and the AEM context setup should match the structure and resource paths in your project for the test to run successfully.
Views
Replies
Total Likes
Hi @ManviSharma, I updated the current resource to /content/country-list/countryItems but countryList.getCountryList() still NULL. Thanks.
Views
Replies
Total Likes
You are using Model class with name (adaptor class name ) CountryList.class and in createModel you are using CountryListImpl.class please correct -
countryList = Objects.requireNonNull(ctx.getService(ModelFactory.class)) .createModel(ctx.request(), CountryListImpl.class);
to
countryList = Objects.requireNonNull(ctx.getService(ModelFactory.class))
.createModel(ctx.request(), CountryList.class);
Views
Replies
Total Likes
Hi @Nishant-Singh, countryList is an object of CountryListImpl (not CountryItem), CountryItem will response ClassCastException.
Views
Replies
Total Likes
you need to create object by adapting method
countyList = aemContext.request().adaptTo(CountryList.class);
Views
Replies
Total Likes
Hi @Mahedi_Sabuj ,
Just checked your problem. You are using Sling Deligation Pattern, I have an article on how to write unit tests for this type of Sling Model: https://medium.com/@sadyrifat/aem-unit-test-case-for-sling-delegation-pattern-1f011b947fb3
The way you are approaching is somehow wrong and you will get trouble on this.
By using this article your Unit test will be like this:
private final AemContext context = new AemContextBuilder(ResourceResolverType.JCR_MOCK).plugin(CORE_COMPONENTS).build();
private CountryList countryList;
@BeforeEach
void init() {
Resource pageResource = context.load().json("/com/aem-demo/core/internal/models/page.json", "/content/uk");
Page page = pageResource.getChild("en/product").adaptTo(Page.class);
Map<String, Object> jsonObject = TestUtils.getJsonObject("/com/aem-demo/core/internal/models/country-list.json");
context.currentResource(context.create().resource(page, "countryList", jsonObject));
countryList = context.request().adaptTo(CountryListImpl.class);
}
@Test
void countryList() {
assertNotNull(countryList);
}
And most importantly your json will be this,
{
"jcr:primaryType": "nt:unstructured",
"sling:resourceType": "awc-demo/components/country-list",
"title": "Select ...",
"countryItems": {
"jcr:primaryType": "nt:unstructured",
"item0": {
"jcr:primaryType": "nt:unstructured",
"country": "Austria",
"link": "/content/aem-demo/at/de",
"language": "German"
},
"item1": {
"jcr:primaryType": "nt:unstructured",
"country": "Canada",
"link": "/content/aem-demo/ca/en",
"language": "English"
}
}
}
After running your code with this setup I am getting the clildresources.
Hope this helps you.
Hi @Sady_Rifat, In Sling Model countryList return value as expected, issue is on Unit Test. Let me check your implementation, I will get back to you soon.
Views
Replies
Total Likes
I've identified a simplified solution, and I only need to implement two modifications.
1. CountryListImpl: change adaptables to Resource.Class
@Model(
adaptables = Resource.class,
adapters = { CountryLinkButton.class },
resourceType = CountryLinkButtonImpl.RESOURCE_TYPE,
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
@Exporter(
name = ExporterConstants.SLING_MODEL_EXPORTER_NAME,
extensions = ExporterConstants.SLING_MODEL_EXTENSION
)
public class CountryLinkButtonImpl implements CountryLinkButton {
.....
}
2. CountryListImplTest: Update createModel
createModel(ctx.request().getResource(), CountryListImpl.class);
Reference: https://medium.com/@veena.vikraman19/aem-junit-for-simple-aem-component-with-multifield-2326f61c8982
Views
Replies
Total Likes