Expand my Community achievements bar.

Guidelines for the Responsible Use of Generative AI in the Experience Cloud Community.

@Inject annotation in AEM JUnit

Avatar

Level 2

Hi all,
I have many Sling Models and I need to create a JUnit test for them, but I'am not able to deal with the @inject  annotation.
Here a simple example:
Sling Model:

import lombok.Getter;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;

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

protected final Logger logger = LoggerFactory.getLogger(getClass());

@Inject
protected Resource currentResource;

@Getter
private List<Resource> items;

@PostConstruct
protected void init() {
items = new ArrayList<>();
try {
Resource parsys = currentResource.getChild("parsys");
if (parsys != null) {
for(Resource child : parsys.getChildren()) {
items.add(child);
}
}
} catch (Exception ex) {
logger.error("Unexpected error: ", ex);
}
}

}

Test:

import io.wcm.testing.mock.aem.junit5.AemContext;
import io.wcm.testing.mock.aem.junit5.AemContextBuilder;
import io.wcm.testing.mock.aem.junit5.AemContextExtension;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.impl.injectors.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

@ExtendWith({AemContextExtension.class, MockitoExtension.class})
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");
currentResource = context.currentResource("/content/myproject/us/jcr:content/simple-component");
context.request().setResource(currentResource);
simpleModel = context.request().adaptTo(SimpleModel.class);
}

@Test
void testGetRowsItems() {
//Build expected result
Resource parsys = currentResource.getChild("parsys");
List<Resource> expectedResult = new ImmutableList.Builder<Resource>()
.add(parsys.getChild("item1"))
.add(parsys.getChild("item2"))
.build();

//Build actual result
List<Resource> actualResult = simpleModel.getItems();
//Check the actual result
assertEquals(expectedResult.size(), actualResult.size());
}

}

The actualResult.size() gives me 0 because the @inject currentResource is not "executed" (currentResource is null).

If I change the annotation from @inject to @SlingObject the test goes well but of course I want that my tests work with @inject annotation.
Could anyone help me?
Thanks!

4 Replies

Avatar

Employee Advisor

try this : 

Resource resource = context.resourceResolver().getResource("/content/sample/en");

 reference : https://wcm.io/testing/aem-mock/usage.html

 

Avatar

Level 2

Thank you for your reply, but it doesn't seem to work since I received the same result.
Below the code that I have tried:

context.load().json("SimpleModelTest.json", "/content/myproject/us");
currentResource = context.resourceResolver().getResource("/content/myproject/us/jcr:content/simple-component");
//Test 1
simpleModel
= context.request().adaptTo(SimpleModel.class);
//Test 2
simpleModel = currentResource.adaptTo(SimpleModel.class);

 

 

Avatar

Employee Advisor

I did a small setup. I am able to get the resource object

ExampleTest:

 

package com.adobe.aem.guides.wknd.core.models;

 

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 com.day.cq.wcm.api.Page;

 

import io.wcm.testing.mock.aem.junit5.AemContext;

import io.wcm.testing.mock.aem.junit5.AemContextExtension;

 

@ExtendWith(AemContextExtension.class)

public class ExampleTest {

 

  private final AemContext context = new AemContext();

 

  @BeforeEach

  public void setUp() throws Exception {

    context.load().json("/com/adobe/aem/guides/wknd/core/models/impl/sample-data.json", "/content/sample/en");

  }

 

  @test

  public void testSomething() {

    Resource resource = context.resourceResolver().getResource("/content/sample/en");

    Page page = resource.adaptTo(Page.class);

    // further testing

  }

 

}

 

sample-data.json:

 

{

  "jcr:primaryType": "cq:Page",

  "jcr:content": {

    "jcr:primaryType": "cq:PageContent",

    "jcr:title": "English",

    "cq:template": "/apps/sample/templates/homepage",

    "sling:resourceType": "sample/components/homepage",

    "jcr:createdBy": "admin",

    "jcr:created": "Thu Aug 07 2014 16:32:59 GMT+0200",

    "par": {

      "jcr:primaryType": "nt:unstructured",

      "sling:resourceType": "foundation/components/parsys",

      "colctrl": {

        "jcr:primaryType": "nt:unstructured",

        "layout": "2;cq-colctrl-lt0",

        "sling:resourceType": "foundation/components/parsys/colctrl"

      }

    }

  }

}

 

Attached screenshot for reference.

unnamed.jpeg

Avatar

Level 2

Thank you, but my problem is related on the adaptation of the Resource to "SimpleModel" in a test scenario.
In my SimpleModel, I perfom the injection of the currentResource via @inject annotation, but
when I try to test it, the currentResource injected is null and therefore my test fails (simpleModel = currentResource.adaptTo(SimpleModel.class); gives me an instance of SimpleModel with its currentResource instance variable to null).
If I change in SimpleModel from @inject annotation to @SlingObject annotation all works.
In other words, @inject seem to not work and I don't know if I need to mock some OsgiService on @BeforeEach phase or I have some missed dependency in my project.
Thank you a lot for you support.