1

我在测试 Sling 模型时遇到了一些问题:currentPage 由于某种原因没有被注入。

我的 Sling 模型如下所示:

@Model( adaptables = { SlingHttpServletRequest.class, Resource.class }, 
    resourceType = MyModel.RESOURCE_TYPE)
public class MyModel {

   public static final String RESOURCE_TYPE = "myproject/components/renderer";

   @Inject
   private Page currentPage;

   // Model methods, etc.

}

我为它编写了一些 JUnit 测试,如下所示:

@RunWith(MockitoJUnitRunner.class)
public class MyModelTest {

  @Rule
  public final AemContext context = new AemContext();

  @Mock
  private SlingHttpServletRequest request;

  private static final String RESOURCE_PATH = "/content/myproject/jcr:content/myModel";
  private static final String PAGE_PATH = "/content/common/page";

  private MyModel myModel;

  @Before
  public final void setUp() throws Exception {
    context.load().json("/models/MyModel.json",RESOURCE_PATH);
    context.load().json("/common-page.json", PAGE_PATH);

    Resource pageResource = context.resourceResolver().getResource(PAGE_PATH);
    Page page = pageResource.adaptTo(Page.class);

    context.currentPage(page);
    context.addModelsForClasses(MyModel.class);
    when(request.getResource()).thenReturn(context.resourceResolver().getResource(RESOURCE_PATH));
    myModel = request.getResource().adaptTo(MyModel.class);
  }

  @Test
  public void simpleLoadTest(){
    assertNotNull(myModel);
  }   
}

这是我得到的错误:

   [main] WARN org.apache.sling.models.impl.ModelAdapterFactory - Could not adapt to model
  org.apache.sling.models.factory.MissingElementsException: Could not inject all required fields into class com.myproject.common.core.models.MyModel
   at org.apache.sling.models.impl.ModelAdapterFactory.createObject(ModelAdapterFactory.java:558)
   at org.apache.sling.models.impl.ModelAdapterFactory.internalCreateModel(ModelAdapterFactory.java:319)
   at org.apache.sling.models.impl.ModelAdapterFactory.getAdapter(ModelAdapterFactory.java:195)
   at org.apache.sling.testing.mock.sling.MockAdapterManagerImpl.getAdapter(MockAdapterManagerImpl.java:146)
   at org.apache.sling.testing.mock.sling.ThreadsafeMockAdapterManagerWrapper.getAdapter(ThreadsafeMockAdapterManagerWrapper.java:46)
   at org.apache.sling.api.adapter.SlingAdaptable.adaptTo(SlingAdaptable.java:104)
   at org.apache.sling.testing.resourceresolver.MockResource.adaptTo(MockResource.java:110)
   at uk.co.restaurants.common.core.models.MyModelTest.setUp(MyModelTest.java:44)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
   at java.lang.reflect.Method.invoke(Method.java:498)
   at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
   at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
   at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
   at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
   at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:48)
   at org.junit.rules.RunRules.evaluate(RunRules.java:20)
   at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
   at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
   at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
   at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
   at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
   at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
   at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
   at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
   at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
   at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)
   at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)
   at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
   at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
   Suppressed: org.apache.sling.models.factory.MissingElementException: Could not inject private com.day.cq.wcm.api.Page com.myproject.common.core.models.MyModel.currentPage
       at org.apache.sling.models.impl.ModelAdapterFactory.createObject(ModelAdapterFactory.java:562)
       ... 34 more
   Caused by: org.apache.sling.models.factory.ModelClassException: No injector returned a non-null value!
       at org.apache.sling.models.impl.ModelAdapterFactory.injectElement(ModelAdapterFactory.java:482)
       at org.apache.sling.models.impl.ModelAdapterFactory.createObject(ModelAdapterFactory.java:560)
       ... 34 more

对于其他一些 Sling 模型测试,注入效果很好,尽管对于 currentPage 我不确定如何继续。我也找不到关于在 Sling 模型中模拟 currentPage 对象的文档。

任何帮助将不胜感激。


更新

以下评论有助于更好地理解该测试的外观。我做了一些更改,但我的测试仍然失败。现在这些类看起来像这样:

@RunWith(MockitoJUnitRunner.class)
public class MyModelTest {

  @Rule
  public final AemContext context = new AemContext();

  @Mock
  private SlingHttpServletRequest request;

  @Mock
  AemObjectAnnotationProcessorFactory factory;

  @InjectMocks
  AemObjectInjector aemObjectInjector;

  private static final String RESOURCE_PATH = "/content/myproject/jcr:content/mymodel";
  private static final String PAGE_PATH = "/content/common/page";

  private MyModel mymodel;

  @Before
  public final void setUp() throws Exception {
    context.load().json("/common-page.json", PAGE_PATH);
    Resource pageResource = context.resourceResolver().getResource(PAGE_PATH);
    Page page = pageResource.adaptTo(Page.class);
    context.currentPage(page);

    context.load().json("/models/MyModel.json",RESOURCE_PATH);
    context.request().setServletPath(RESOURCE_PATH);
    context.registerInjectActivateService(factory);
    context.registerService(AemObjectInjector.class, aemObjectInjector);            

    Mockito.when(request.getResource())
      .thenReturn(context.resourceResolver().getResource(RESOURCE_PATH));
    Resource resource = request.getResource();
    mymodel = resource.adaptTo(MyModel.class);
  }

  @Test
  public void simpleLoadTest(){
      assertNotNull(mymodel);
  }  

}

以及带有特定喷油器的更新模型:

@Model(
   adaptables = { SlingHttpServletRequest.class }, 
   resourceType = MyModel.RESOURCE_TYPE)
public class MyModel {

  public static final String RESOURCE_TYPE = "myproject/components/renderer";

  @AemObject
  private Page currentPage;

  // Model methods, etc.

}

setUp() 方法不会抛出任何异常,也不会发出任何警告。变量 mymodel 为空,所以我仍然在这里遗漏了一些东西。


更新 2

我将代码推送到 Github,您可以在以下 URL https://github.com/josebercianowhitbread/myproject中找到该项目

笔记:

- 在 AEM 6.3 中进行了测试

- 像往常一样部署项目: mvn clean install -PautoInstallPackage

-该项目添加了一些示例页面以确保 Sling 模型按预期工作

- Sling 模型的功能非常简单:它沿着内容树向上爬,直到找到“isRootPage”属性设置为 true 的父节点。

你可能有任何问题让我知道。

提前感谢您提供的任何帮助。


更新 3

Justin Edelson 善意地纠正并提供了测试代码。非常感谢他和 Ahmed Musallam,他一直在追逐这篇文章,直到他确保一切正常:)

我的初始代码的两个主要问题是:我试图模拟 Slick 请求,但应该使用来自 AemContext 的请求。该模型未注册。

public class MyModelTest {

   @Rule
   public final AemContext context = new AemContext();

   private MockSlingHttpServletRequest request;

   AemObjectAnnotationProcessorFactory factory = new AemObjectAnnotationProcessorFactory();

   AemObjectInjector aemObjectInjector = new AemObjectInjector();

   private static final String RESOURCE_PATH = "/content/parent-page/jcr:content/content/renderer";
   private static final String PAGE_PATH = "/content/parent-page";

   private MyModel mymodel;

   @Before
   public final void setUp() throws Exception {
       request = context.request();
       context.addModelsForClasses(MyModel.class);
       context.load().json("/pages/common-page.json", PAGE_PATH);
       Resource pageResource = 
       context.resourceResolver().getResource(PAGE_PATH);

       Page page = pageResource.adaptTo(Page.class);
       context.currentPage(page);

       context.load().json("/models/MyModel.json", RESOURCE_PATH);
       context.registerInjectActivateService(factory);
       context.registerService(AemObjectInjector.class, aemObjectInjector);


       request.setResource(context.resourceResolver()
         .getResource(RESOURCE_PATH));
       mymodel = request.adaptTo(MyModel.class);
   }

   @Test
   public void simpleLoadTest() {
       assertNotNull(mymodel);
   }

}

4

2 回答 2

3

您依赖的是 ACS 的@AemObject喷油器。请记住,该注入器和任何 sling 注入器都是 OSGI 服务,并且您的 AEM 上下文没有注册该服务,即:它不知道 AemObjectInjector,这就是为什么您永远不会获得非空值的原因Page

您需要注册注入器和注释处理器:

要注册服务,请查看 wcm.io 的文档:注册 OSGi 服务

注意:注册这些服务时,请确保您注册的是服务的真实实例,而不是模拟实例。您需要真正的 impl 才能正确进行 sling 模型注入:

aemObjectInjector = new AemObjectInjector() context.registerService(AemObjectInjector.class, aemObjectInjector);

=================================================

更新:

在查看了您在此处提供的简单存储库后,我查看并修复了测试以使其像您希望它在此处的存储库的分支中一样工作

为了其他人,这里是:模型类,测试类和json资源:

MyModel.java:

package com.myproject.models;

import javax.annotation.PostConstruct;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.models.annotations.Model;

import com.adobe.acs.commons.models.injectors.annotation.AemObject;
import com.day.cq.wcm.api.Page;

@Model(
    adaptables = { SlingHttpServletRequest.class },
    resourceType = MyModel.RESOURCE_TYPE)
public class MyModel {

    public static final String RESOURCE_TYPE = "myproject/components/renderer";

    @AemObject
    private Page currentPage;

    protected final String ROOT_PAGE_PROPERTY = "isRootPage";
    private Page rootPage;

    @PostConstruct
    private void initModel() {
        // Fetches the root language page in order to get the data from that node.
        while (!isRootPage(currentPage)) {
            currentPage = currentPage.getParent();
        }
        rootPage = currentPage;
    }

    private boolean isRootPage(Page selectedPage) {
        return selectedPage.getProperties().get(ROOT_PAGE_PROPERTY, false);
    }

    public String getRootPath() {
            return rootPage.getPath();
    }

}

这是测试类:MyModelTest.java

package com.myproject.models;

import static org.junit.Assert.*;

import org.apache.sling.api.SlingHttpServletRequest;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import com.adobe.acs.commons.models.injectors.annotation.impl.AemObjectAnnotationProcessorFactory;
import com.adobe.acs.commons.models.injectors.impl.AemObjectInjector;

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


@RunWith(MockitoJUnitRunner.class)
public class MyModelTest {

    @Rule
    public final AemContext context = new AemContext();

    @Mock
    private SlingHttpServletRequest request;


    private static final String RESOURCE_PATH = "/content/parent-page/jcr:content/content/renderer";
    private static final String PAGE_PATH = "/content/parent-page";

    private MyModel mymodel;
    private AemObjectInjector aemObjectInjector;
    private AemObjectAnnotationProcessorFactory factory;

    @Before
    public final void setUp() throws Exception {

        // register model
        // NOTE: this is the alternative to creating an adapter/adapter factory.
        context.addModelsForClasses(MyModel.class);

        // load page and resource from json
        context.load().json("/pages/common-page.json", PAGE_PATH);
        context.load().json("/models/MyModel.json", RESOURCE_PATH);

        // set current page to the page path
        context.currentPage(PAGE_PATH);

        // register ACS AemObjectInjector service
        aemObjectInjector = new AemObjectInjector();
        context.registerService(AemObjectInjector.class, aemObjectInjector);

        // adapt request to model
        mymodel = context.request().adaptTo(MyModel.class);
    }

    @Test
    public void simpleLoadTest() {
        // mymodel is NOT null
        assertNotNull(mymodel);
        // mymodel's page has property 'isRootPage=true', therefor it's the root page
        assertEquals(mymodel.getRootPath(), PAGE_PATH);
    }
}

json资源如下:

MyModel.json

{
    "jcr:primaryType": "nt:unstructured",
    "sling:resourceType": "myproject/components/renderer"
}

common-page.json

{
    "jcr:primaryType": "cq:Page",
    "jcr:createdBy": "admin",
    "jcr:created": "Fri Nov 03 2017 13:56:12 GMT+0000",
    "jcr:content":
    {
        "jcr:primaryType": "cq:PageContent",
        "jcr:createdBy": "admin",
        "jcr:title": "Parent page",
        "cq:template": "/apps/myproject/templates/common-page",
        "isRootPage": true,
        "jcr:created": "Fri Nov 03 2017 13:56:12 GMT+0000",
        "cq:lastModified": "Fri Nov 03 2017 13:56:12 GMT+0000",
        "sling:resourceType": "myproject/components/page",
        "cq:lastModifiedBy": "admin"
    }
}
于 2017-10-30T15:59:04.767 回答
1

在模型中使用@AemObject(from com.adobe.acs.commons.models.injectors.annotation.AemObject) 而不是@Inject注释,以便成功注入当前页面。

文档链接

于 2017-10-30T09:02:18.087 回答