调用AEM模型@PostConstruct方法时出现NullPointerException问题排查
AEM模型单元测试空指针异常排查
我尝试用AemContext将JSON资源文件适配到AEM模型,通过@PostConstruct注解初始化模型方法。但运行测试时,调用测试类中的getList()函数总会出现NullPointerException。我正在学习对这类依赖@PostConstruct注入的模型进行单元测试,希望了解出现空指针异常的原因。
模型接口
package com.xyz.core.models; import java.util.List; import java.util.Map; public interface TableModel { /** * Function to be implemented to fetch * list of table items from JCR * * @return list of items */ public List<Map<String, String>> getList(); }
模型实现类
package com.xyz.core.models.impl; import com.xyz.core.models.TableModel; import javax.annotation.PostConstruct; import javax.inject.Inject; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ValueMap; import org.apache.sling.models.annotations.Model; import org.apache.sling.models.annotations.Optional; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @Model(adaptables = Resource.class, adapters = TableModel.class) public class TableModelImpl implements TableModel{ @Inject @Optional private Resource items; private List<Map<String, String>> itemsChildren = new ArrayList<Map<String, String>>(); @PostConstruct public void init(){ if (items != null) { for (Resource resource: items.getChildren()) { ValueMap properties = resource.adaptTo(ValueMap.class); String title = properties.get("title", String.class); String content = properties.get("content", String.class); Map<String, String> map = new HashMap<String, String>(); map.put("title", title); map.put("content", content); itemsChildren.add(map); } } } public List<Map<String, String>> getList() { return Collections.unmodifiableList(itemsChildren); } }
模型测试类
package com.xyz.core.models; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Map; import java.util.HashMap; import java.util.List; import java.util.ArrayList; import org.junit.jupiter.api.BeforeEach; import com.xyz.core.models.impl.TableModelImpl; import io.wcm.testing.mock.aem.junit5.AemContext; import io.wcm.testing.mock.aem.junit5.AemContextExtension; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith({AemContextExtension.class, MockitoExtension.class}) public class TableModelImplTest { private final AemContext aemContext = new AemContext(); TableModelImpl tableModelImpl; private List<Map<String, String>> expectedItemsChildren = new ArrayList<Map<String, String>>(); private final Map<String, String> map = new HashMap<String, String>(); @BeforeEach void setUp() { aemContext.addModelsForClasses(TableModelImpl.class); aemContext.load().json("/com/xyz/core/models/impl/Table.json", "/component"); } @Test void getList() { aemContext.currentResource("/component/table"); tableModelImpl = aemContext.request().adaptTo(TableModelImpl.class); map.put("title", "TItle"); map.put("content","content"); this.expectedItemsChildren.add(map); map.put("title", "Many"); map.put("content","More"); List<Map<String, String>> actualItemsChildren = tableModelImpl.getList(); assertEquals(expectedItemsChildren, actualItemsChildren); } }
资源JSON文件
{ "table":{ "jcr:primaryType":"nt:unstructured", "jcr:createdBy":"admin", "jcr:lastModifiedBy":"admin", "jcr:created":"Wed Aug 24 2022 10:38:41 GMT+0900", "jcr:lastModified":"Wed Aug 24 2022 10:39:03 GMT+0900", "sling:resourceType":"xyz/components/table", "items":{ "jcr:primaryType":"nt:unstructured", "item0":{ "jcr:primaryType":"nt:unstructured", "title":"TItle", "content":"Content" }, "item1":{ "jcr:primaryType":"nt:unstructured", "title":"Many", "content":"More" } } } }
空指针异常原因分析
模型适配方式错误
TableModelImpl通过@Model注解声明了adaptables = Resource.class,意味着这个模型只能从Resource对象适配得到。但测试代码中使用aemContext.request().adaptTo(TableModelImpl.class)尝试从Request对象适配模型,这会导致tableModelImpl为null,后续调用getList()自然触发空指针异常。预期数据构造逻辑错误(非空指针直接原因,但会导致断言失败)
测试中复用了同一个map对象,先添加到expectedItemsChildren后又修改其内容,最终预期列表里的两个元素其实是同一个对象,而模型返回的是两个独立的Map对象,会导致断言比对失败。
修复方案
修正模型适配方式
将测试中的适配代码改为从当前Resource对象适配:tableModelImpl = aemContext.currentResource().adaptTo(TableModelImpl.class);修正预期数据构造
为每个预期条目创建独立的Map对象,同时保证内容和JSON文件一致:Map<String, String> map1 = new HashMap<>(); map1.put("title", "TItle"); map1.put("content","Content"); expectedItemsChildren.add(map1); Map<String, String> map2 = new HashMap<>(); map2.put("title", "Many"); map2.put("content","More"); expectedItemsChildren.add(map2);移除不必要的依赖
测试类中没有使用Mockito的任何功能,可以去掉MockitoExtension的扩展声明:@ExtendWith(AemContextExtension.class)
内容的提问来源于stack exchange,提问作者Anim
相关产品推荐
相关产品推荐

