AEM Mocks - Sling Model testing - CAConfig field is always null

756 views
Skip to first unread message

Basic Danijel

unread,
Aug 12, 2021, 10:31:01 AM8/12/21
to wcm-i...@googlegroups.com
Hi everybody,

I'm trying to test the logic inside a Sling Model that uses CAConfig by using AEM Mocks. It injects it in a field (via Self annotation). Unfortunately, the outcome is that the field always has null value.

Could somebody give me some hints?

Here's what I've done so far:

Added maven dependencies in a project:
io.wcm:io.wcm.testing.wcm-io-mock.caconfig:1.1.0
org.apache.sling:org.apache.sling.testing.caconfig-mock-plugin:1.3.2
io.wcm:io.wcm.testing.aem-mock.junit5:2.5.2

I've also added io.wcm:io.wcm.testing.aem-mock:2.5.2 but still the issue remains.

A custom Sling Model:
@Model(
adaptables = SlingHttpServletRequest.class,
adapters = CustomModel.class,
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class CustomModelImpl implements CustomModel {
  
  @Self
  private SiteConfiguration siteConfiguration; // caconfig

  // a logic that is using siteConfiguration
  public String aMethodThatUsesCAConfig() {
    return Optional.ofNullable(siteConfiguration).map(SiteConfiguration::prop).orElse("empty");
  }
}
public interface CustomModel {
  String aMethodThatUsesCAConfig();
}

A custom CAConfig:
org.apache.sling.caconfig.annotation.Configuration(label="...", description="...")
public @interface SiteConfiguration {
  org.apache.sling.caconfig.annotation.Property(...)
  String prop();
}

Test artifacts:
- JSON that holds CAConf JCR structure: caconfig.json
- JSON that holds a sample content for JCR repository: content.json
Test class:
@ExtendWith(AemContextExtension.class)
class CustomModelImplTest {
  private AemContext context =  new AemContextBuilder()
    .plugin(org.apache.sling.testing.mock.caconfig.ContextPlugins.CACONFIG)
    .plugin(io.wcm.testing.mock.wcmio.caconfig.ContextPlugins.WCMIO_CACONFIG)
    .build();

  @BeforeClass
  void setUp() {
    context.addModelsForPackage("sample.model.package");
    context.load().json("/path/to/content.json", "/content");
    context.load().json("/path/to/caconfig.json", "/conf");
    context.currentPage("/content/site/region/en/pages/sample-page");
    context.currentResource("/content/site/region/en/pages/sample-page/jcr:content/root/container/res");
  }
  @Test
  void testSomeMethod() {
    MockContextAwareConfig.registerAnnotationClasses(context, SiteConfiguration.class);
    MockCAConfig.contextPathStrategyAbsoluteParent(context, 3); // /content/site/region/en

   CustomModel model = context.request().adaptTo(CustomModel.class);
   assertNotNull(model); // OK
   assertEquals("prop val", model.aMethodThatUsesCAConfig()) // NOT OK because siteConfiguration is null
   assertEquals("empty", model.aMethodThatUsesCAConfig()) // OK
  }
}

Thank you.

Best regards,
Danijel

Kuijpers, Henry

unread,
Aug 13, 2021, 3:46:17 AM8/13/21
to wcm-i...@googlegroups.com

Hi Danijel,

 

Is this working at runtime (in AEM)? I don’t believe it is working.

 

The reason is:

  • You’re using the SelfInjector to obtain an instance of CAConfig, that will not work, at all, and yields a null value;
  • You’re using defaultInjectionStrategy=OPTIONAL, so while normally instantiation of your model would fail due to the config not being able to be injected, it will now succeed, but as a consequence, use the null value.

 

So in short:

  1. defaultInjectionStrategy=OPTIONAL is an anti-pattern, do not use it (I would have loved it to not exist, so that we always have to be explicit on every field! As you can see, it doesn’t only apply to a simple @ValueMapValue, but to everything, @Self, @OSGiService, …. etc – Therefore now masking your error)
  2. Using the SelfInjector to get a CAConfig value will not work – you will have to use the ConfigurationBuilder (which you can adapt from a resource / page / …) to obtain your config
  3. Beware of using the resource to obtain a configurationbuilder – In the case of editable templates, that resource will point to /conf, instead of /content – An alternative is to use the current page (which should always point to /content) instead

 

 

Met vriendelijke groet / Kind regards,

 

Henry Kuijpers

Architect / Consultant DXM

Amplexor – Embrace the Future

T: +31 40 250 79 00 | M: +31 6 83 67 45 83

Steijgerweg 8 | 5616HS Eindhoven | The Netherlands

signature_766440696  signature_869647427  signature_1864440851  signature_1042136542  signature_1921299468  signature_850203772

 

--
You received this message because you are subscribed to the Google Groups "wcm-io Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to wcm-io-dev+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/wcm-io-dev/CAHy%3D9eAot9A9nAPYhn2ZXGr0ZAbxfQ4Gd-frVv62oOiceuakAA%40mail.gmail.com.

This email message is confidential and for the sole use of the intended recipient(s). For more information on our email security procedures and how we protect your data, check our www.amplexor.com/privacy. | Cet e-mail est confidentiel et pour l'usage exclusif du (des) destinataire(s) prévu(s). Pour plus d'informations sur nos procédures de sécurité relatives aux e-mails et à la manière dont nous protégeons vos données, veuillez consulter notre www.amplexor.com/politique-confidentialite.

Basic Danijel

unread,
Aug 13, 2021, 4:55:40 AM8/13/21
to wcm-i...@googlegroups.com
Hi Henry,

Thank you for your reply.

I can confirm that the current code (with SelfInjector) works in the runtime.

The original code that I'm writing JUnit test for (the one I wrote above is the sample code just to explain my intention and it's similar to the original code) does not use "optional" as the default injection strategy. I've just modified it a bit in order to be able to debug a post-construct method of a Sling model when the JUnit test is executed.

Ok, I'll try to refactor the code to use ConfigurationBuilder as you suggested. Yesterday when searching for a solution for the problem I have, I found out this sample code https://github.com/apache/sling-org-apache-sling-testing-caconfig-mock-plugin/blob/master/src/test/java/org/apache/sling/testing/mock/caconfig/ContextPluginsTest.java , so I suppose I should follow that way.

Best regards,
Danijel 

Stefan Seifert

unread,
Aug 13, 2021, 5:37:13 AM8/13/21
to wcm-i...@googlegroups.com

hello danijel.

 

i agree with henry that normally self-injecting a CAConfig (in your case adapting request to a specific CAconfig) should not work out of the box.

probably in your project there is a custom AdapterFactory in place that is doing this adaptation for convenience?

in this case you just have to register this AdapterFactory OSGi service in your test context as well.

 

stefan

 

 

Basic Danijel

unread,
Aug 13, 2021, 9:40:04 AM8/13/21
to wcm-i...@googlegroups.com
Hi Stefan,

Yes, you are right. I’ve just checked out and there’s the one.

Ok, will extend the test as you suggested.

Thank you.

Best regards,
Danijel

Reply all
Reply to author
Forward
0 new messages