AEM - JUnit @Inject annotation | Community
Skip to main content
Level 2
February 10, 2022
Solved

AEM - JUnit @Inject annotation

  • February 10, 2022
  • 2 replies
  • 7293 views

Hi all,
I’am facing an issue regarding the handling of the @586265 annotation with AEM-Junit test scenario.
Here an example of the issue:
Model Class:

 

@Model(adaptables = {SlingHttpServletRequest.class}, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) public class SimpleModel { @586265 private Resource currentResource; public Resource getCurrentResource(){ return currentResource; } }

 

Test Class:

 

@ExtendWith({AemContextExtension.class, MockitoExtension.class}) public class SimpleModelTest{ private AemContext context = new AemContext(); private SimpleModel simpleModel; @BeforeEach public void setup() throws Exception { context.addModelsForClasses(SimpleModel.class); context.load().json("SimpleModelTest.json", "/content/myproject/us"); } @2785667 void testResource() { context.currentResource("/content/myproject/us/jcr:content/simple-component"); simpleModel = context.request().adaptTo(SimpleModel.class); assertNotNull(simpleModel.getCurrentResource()); } }

 

The test fails since simpleModel.getCurrentResource() gives me a null resource.
If I change the @586265 annotation in the SimpleModel class with @SlingObject the test doesn't fail, but I don’t want to change every annotation with SlingObject or the similar one.

Could you help me to solve this issue?

This post is no longer active and is closed to new replies. Need help? Start a new post to ask your question.
Best answer by joerghoh

Hi @joerghoh ,
thank you for your suggestion. Here what I have obtained:

Model

import lombok.Getter; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.resource.Resource;
import com.day.cq.wcm.api.Page; import org.apache.sling.models.annotations.DefaultInjectionStrategy; import org.apache.sling.models.annotations.Model; import javax.annotation.PostConstruct; import javax.inject.Inject; @Model(adaptables = {SlingHttpServletRequest.class}, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) public class SimpleModel { @Inject private Resource currentResource;

@Inject
private Page currentPage;
@Getter private String resourceName;

@Getter
private String pagePath;
@PostConstruct protected void init() { resourceName = currentResource.getName();
pagePath = currentPage.getPath(); } }

 

Test
import io.wcm.testing.mock.aem.junit5.AemContext;
import org.apache.sling.api.resource.Resource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import com.day.cq.wcm.api.Page;
import io.wcm.testing.mock.aem.junit5.AemContextExtension; import org.mockito.junit.jupiter.MockitoExtension; import static junit.framework.Assert.assertEquals; import static org.mockito.Mockito.when; @ExtendWith({AemContextExtension.class, MockitoExtension.class})
@RunWith(MockitoJUnitRunner.class) public class SimpleModelTest { private final AemContext aemContext=new AemContext(); @BeforeEach public void setup() throws Exception { context.load().json("SimpleModelTest.json", "/content/page");
context.registerService(Resource.class, context.currentResource("/content/page/jcr:content/mycomponent"));
context.registerService(Page.class, context.currentPage("/content/page"));
} @Test void testResource() {
SimpleModel simpleModel = context.request().adaptTo(SimpleModel.class); assertEquals("/content/page", simpleModel.getPagePath());
assertEquals("mycomponent", simpleModel.getResourceName()); } }

 

 

The registerService allows @Inject annotation to inject Resource and Page by finding the right Injector.
Of course this need to be done for every "types" of inject (resourceResolver etc), but seems to be more elegant respect to the "setter and mocker" approach.
What do you think about this solution?


Thank you for your time and support!


My thinking is that it should not need these statements. Also a page injector is not default (IIRC it is a wcm.io feature).

 

Have you tried to switch to this:

@Model(adaptables = {Resource.class}, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class SimpleModel  {
...
}

If you don't need any information from the request (e.g. a parameter) this is much more flexible.

2 replies

milind_bachani
Adobe Employee
Adobe Employee
February 10, 2022

Hi @federicov798870 ,

 

There were multiple errors , I am pasting the working code here :

SimpleModel.class:

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;

import javax.inject.Inject;

@Model(adaptables = {SlingHttpServletRequest.class}, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class SimpleModel  {

    @Inject
    public Resource currentResource;

    public Resource getCurrentResource(){
        return currentResource;
    }

}
SimpleModelTest.class

import io.wcm.testing.mock.aem.junit5.AemContext;
import io.wcm.testing.mock.aem.junit5.AemContextExtension;
import junit.framework.Assert;
import org.apache.sling.api.resource.Resource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

import static junit.framework.Assert.assertNotNull;

@ExtendWith({AemContextExtension.class, MockitoExtension.class})
public class SimpleModelTest{

    private AemContext context = new AemContext();
    private SimpleModel simpleModel;

    @BeforeEach
    public void setup() throws Exception {
        context.addModelsForClasses(SimpleModel.class);
        context.load().json("/com/aem/geeks/core/models/impl/Author.json", "/content/myproject/us");
    }

    @Test
    void testResource() {
        Resource resource = context.currentResource("/content/myproject/us/jcr:content/simple-component");
        simpleModel = context.request().adaptTo(SimpleModel.class);
        simpleModel.currentResource = resource;        Assert.assertNotNull(simpleModel.getCurrentResource());
    }

}
Author.json
{
  "jcr:content":{
    "jcr:primaryType":"cq:PageContent",
    "jcr:createdBy":"admin",
    "jcr:title":"Author Bio",
    "cq:contextHubSegmentsPath":"/etc/segmentation/contexthub",
    "jcr:created":"Tue Oct 27 2020 22:11:34 GMT+0530",
    "cq:lastModified":"Mon Nov 02 2020 23:25:43 GMT+0530",
    "cq:contextHubPath":"/etc/cloudsettings/default/contexthub",
    "cq:lastModifiedBy":"admin",
    "simple-component": {
      "random": "test"
    }
  }
}

I have highlisted the mistakes in bold, also for junits you need to command the code what to pickup, in your case you were not commanding the model which resource to be picked up which is why it always returned null.

 

Reference : https://www.youtube.com/watch?v=g5x6F8bUHj8

 

Thanks.

Level 2
February 10, 2022

Hi @milind_bachani ,
thank you for your reply.
I'am looking for a solution in which I don't need to perform resource setter by my hand.
If you take the same code that you have posted, and you remove this line:
"simpleModel.currentResource = resource;" and change the @Inject annotation with @SlingObject annotation in the SlingModel, the adaptTo works without the need to set resource.
Is it possibile for you to achive the same result with @Inject annotation?
Thank you again for your reply,
Federico


milind_bachani
Adobe Employee
Adobe Employee
February 10, 2022

@federicov798870 Ah okay, I understood what's the ask - however, is there any particular reason you dont want to set the object manually ?

joerghoh
Adobe Employee
Adobe Employee
February 12, 2022

Technically your code looks correct, and I am also not aware of a problem or missing piece of functiionality in that area.

 

What looks strange is that you can only adapt from a request to the SlingModel (I would change it to make it adaptable from a Resource (only), because this gives you more flexibility to use this model); but then you just set the current resource to that path.

 

have you tried already something like this:

    @test
    void testResource() {
        Resource r = context.resourceResolver.getResource("/content/myproject/us/jcr:content/simple-component");
        simpleModel = r.adaptTo(SimpleModel.class);
        assertNotNull(simpleModel.getCurrentResource());
    }

 

Level 2
February 12, 2022

Hi @joerghoh ,

yes, I tried that. I think that the problem was the @Inject annotation. For istance, if you have a model which Inject the currentPage with the @Inject annotation, the code that you posted doesn't work since the currentPage is null, but if you change the @Inject annotation in your model with another one (e.g. @ScriptVariable) it works. Same result with the other annotations (@SlingObject etc).

Thank you for your time and support.

joerghoh
Adobe Employee
Adobe Employee
February 12, 2022

That means, that just the "@inject" annotation does not work in the tests?

 

Regarding this I found https://medium.com/ida-mediafoundry/sling-models-the-story-behind-inject-c51615164b11 which explains a bit of the magic behind this annotation. 

 

I normally use the other annotations to explicitly express which injector should be used, so I don't have experience with "@inject" in unittests.