Импорт контента из JSON с помощью wcm.io AEM Mocks с реальной реализацией Jackrabbit

Я пытаюсь написать интеграционный тест, используя wcm.io Testing AEM Mocks

Код, который я хочу протестировать, сохраняет некоторые данные в репозитории, поэтому я хотел бы проверить, действительно ли хранится правильный контент после запуска проверенного метода.

В моих предыдущих тестах я использовал JCR_MOCK Тип преобразователя ресурсов, например:

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

    @Rule
    public final AemContext aemContext = new AemContext(ResourceResolverType.JCR_MOCK);

    private MyClass tested;

    @Before
    public void setUp() throws Exception {

        //here, I load an entire page,
        //from which my tested class reads some data
        aemContext.load().json("/jcrdata/myPage.json", "/etc/mystuff/myTool");

        tested = new MyClass(aemContext.resourceResolver());
    }

    @Test
    public void interactWithTheRepository() {

        SomeResult result = tested.interactWithTheRepository();

        //assertions here
    }
}

Это прекрасно работает, пока я просто хочу прочитать данные из фиктивного репозитория или притвориться, что я что-то сохраняю. Проблема начинается, когда я действительно хочу убедиться, что некоторые данные были сохранены в репозитории тестируемым классом.

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

    @Rule
    public final AemContext aemContext = new AemContext(ResourceResolverType.JCR_MOCK);

    private MyClass tested;

    @Before
    public void setUp() throws Exception {

        //here, I load an entire page,
        //on which the instance of my tested class later saves some data
        aemContext.load().json("/jcrdata/myPage.json", "/etc/mystuff/myTool");

        tested = new MyClass(aemContext.resourceResolver());
    }

    @Test
    public void storeDataInTheRepository() {

        tested.storeDataInTheRepository();

        //this comes up as null because the JCR_Mock resolver
        //does not use an actual repository and nothing is stored
        Resource result = aemContext.resourceResolver().getResource("/etc/mystuff/myTool/somethingSavedByMyClassInstance");

        //assertions here
    }
}

Я считаю, что это может быть достигнуто с помощью JCR_JACKRABBIT Типа разрешения ресурсов, который использует настоящий репозиторий. Однако мне трудно загрузить данные в фиктивный репозиторий. Как только я использую JCR_JACKRABBIT вместо JCR_MOCK, мой метод setUp терпит неудачу с NullPointerException (другие части класса опущены для ясности)

@Rule
public final AemContext aemContext = new AemContext(ResourceResolverType.JCR_JACKRABBIT);

@Before
public void setUp() throws Exception {

     //This line fails with an NPE
    aemContext.load().json("/jcrdata/myPage.json", "/etc/mystuff/myTool");

    tested = new MyClass(aemContext.resourceResolver());
}

Я отладил код и заметил, что он терпит неудачу при попытке создать иерархию ресурсов.

java.lang.NullPointerException
    at org.apache.sling.resourceresolver.impl.ResourceResolverImpl.create(ResourceResolverImpl.java:1044)
    at org.apache.sling.testing.mock.sling.loader.ContentLoader.createResourceHierarchy(ContentLoader.java:183)
    at org.apache.sling.testing.mock.sling.loader.ContentLoader.createResourceHierarchy(ContentLoader.java:178)
    at org.apache.sling.testing.mock.sling.loader.ContentLoader.json(ContentLoader.java:155)
    at org.apache.sling.testing.mock.sling.loader.ContentLoader.json(ContentLoader.java:120)
    at com.foo.bar.baz.MyClassTest.setUp(MyClassTest.java:35)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    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:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

Согласно документации, Sling Testing должен обрабатывать создание всех родительских ресурсов при попытке загрузить страницу из документа JSON:

Resource org.apache.sling.testing.mock.sling.loader.ContentLoader.json(String classpathResource, String destPath)

Импортируйте содержимое файла JSON в репозиторий. Автоматически создает родительские иерархии как узлы nt:unstructured, если они отсутствуют.

Однако, когда корень репозитория достигнут и узел etc должен быть создан под /, метод ContentLoader createResourceHierarchy вызывает ResourceResolver со следующими параметрами:

resourceResolver.create(null, ResourceUtil.getName("/etc"), props);

где props — это простое HashMap следующей структуры: {jcr:primaryType=nt:unstructured}, вызывающее NullPointerException (из-за передачи родительского ресурса null)

Я не уверен, является ли это ошибкой или что-то, что я делаю неправильно, в документации Sling Testing упоминается о типе JCR_JACKRABBIT Resource Resolver:

Чтобы импортировать содержимое Sling, вы должны полностью зарегистрировать все типы узлов, необходимые для данных.

но я не уверен, как это интерпретировать.

Я использую:

  • io.wcm.testing.aem-mock 1.2.2
  • org.apache.sling.testing.sling-mock-jackrabbit 0.1.0

Как я могу загрузить содержимое из файла JSON при использовании реализации JCR_JACKRABBIT Resource Resolver?


person toniedzwiedz    schedule 19.01.2015    source источник


Ответы (2)


Благодаря ответу Томаса я смог сделать пару шагов вперед, но продолжал сталкиваться с разными ошибками. Тем временем я открыл тему на сайте wcm.io Developers. группу рассылки, чтобы узнать, возможен ли более доступный способ достижения моих целей. Ответ разделен на две части. Первый описывает, что можно сделать для написания тестов для конкретного варианта использования, который я пытался охватить. Последний описывает мои выводы о фактическом использовании типа преобразователя ресурсов JCR_JACKRABBIT.

Тестирование классов, которые записывают в репозиторий

Оказывается, я слишком быстро переключился с JCR_MOCK на JCR_JACKRABBIT тип преобразователя ресурсов. JCR_MOCK достаточно для моего случая, а именно:

  1. Загрузить содержимое из файла JSON в путь к классам
  2. Выполнить метод тестируемого класса в фиктивном репозитории (предполагается, что класс считывает некоторый контент из репо и сохраняет в нем еще немного)
  3. Прочитайте содержимое, написанное тестируемым классом, и подтвердите, соответствует ли оно ожидаемым значениям.

Данные фактически записываются в хранилище в оперативной памяти. Тот факт, что я не смог его прочитать, был результатом моей собственной ошибки, а не используемой реализации.

Загрузка JSON в AemContext с поддержкой JCR_JACKRABBIT

Мне удалось продвинуться довольно далеко, следуя совету из ответа Томаса, но мне не удалось полностью реализовать эту реализацию. . Вот что я сделал:

Я добавил следующие строки в свой метод setUp:

Session session = aemContext.resourceResolver().adaptTo(Session.class); 
RepositoryUtil.registerNodeType(session, getClass().getResourceAsStream("/nodetypes/types.cnd")); 

незадолго до aemContext.load().json("/jcrdata/myPage.json", "/etc/mystuff/myTool");

types.cnd — это файл в моем пути к классам, который содержит определения типов узлов, найденные в репозитории CRX на моем экземпляре AEM.

Это позволило мне избавиться от NPE, но по какой-то причине типы узлов cq:Page и cq:PageContent не распознаются. Это приводит к тому, что при вызове aemContext.load().json("/jcrdata/myPage.json", "/etc/mystuff/myTool"); выдается PersistenceException, если в загруженном JSON присутствует узел любого из этих типов.

Проблема, похоже, в том, что типы узлов cq:Page и cq:PageContent по-прежнему не могут быть найдены, несмотря на то, что я явно добавляю их в свой файл types.cnd. Я взял определения этих двух типов с сайта docs.adobe.com. и обязательно включил префиксы пространств имен, используемые в этих определениях.

Вот пример трассировки стека, которая появляется, когда у меня есть cq:Page в моем содержимом JSON:

java.lang.RuntimeException: org.apache.sling.api.resource.PersistenceException: Unable to create node at /etc/foo/bar/baz
    at org.apache.sling.testing.mock.sling.loader.ContentLoader.json(ContentLoader.java:167)
    at org.apache.sling.testing.mock.sling.loader.ContentLoader.json(ContentLoader.java:120)
    at com.foo.bar.baz.MyClassTest.setUp(MyClassTest.java:42)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    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.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    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:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: org.apache.sling.api.resource.PersistenceException: Unable to create node at /etc/foo/bar/baz
    at org.apache.sling.jcr.resource.internal.helper.jcr.JcrResourceProvider.create(JcrResourceProvider.java:473)
    at org.apache.sling.resourceresolver.impl.tree.ResourceProviderEntry.create(ResourceProviderEntry.java:479)
    at org.apache.sling.resourceresolver.impl.ResourceResolverImpl.create(ResourceResolverImpl.java:1053)
    at org.apache.sling.testing.mock.sling.loader.ContentLoader.createResource(ContentLoader.java:217)
    at org.apache.sling.testing.mock.sling.loader.ContentLoader.json(ContentLoader.java:163)
    ... 30 more
Caused by: javax.jcr.nodetype.NoSuchNodeTypeException: {http://www.day.com/jcr/cq/1.0}Page
    at org.apache.jackrabbit.core.nodetype.NodeTypeRegistry.getEffectiveNodeType(NodeTypeRegistry.java:1024)
    at org.apache.jackrabbit.core.nodetype.NodeTypeRegistry.getEffectiveNodeType(NodeTypeRegistry.java:487)
    at org.apache.jackrabbit.core.nodetype.NodeTypeManagerImpl.getNodeType(NodeTypeManagerImpl.java:177)
    at org.apache.jackrabbit.core.NodeImpl.addNode(NodeImpl.java:1240)
    at org.apache.jackrabbit.core.session.AddNodeOperation.perform(AddNodeOperation.java:111)
    at org.apache.jackrabbit.core.session.AddNodeOperation.perform(AddNodeOperation.java:37)
    at org.apache.jackrabbit.core.session.SessionState.perform(SessionState.java:200)
    at org.apache.jackrabbit.core.ItemImpl.perform(ItemImpl.java:91)
    at org.apache.jackrabbit.core.NodeImpl.addNodeWithUuid(NodeImpl.java:1769)
    at org.apache.jackrabbit.core.NodeImpl.addNode(NodeImpl.java:1729)
    at org.apache.sling.jcr.resource.internal.helper.jcr.JcrResourceProvider.create(JcrResourceProvider.java:435)
    ... 34 more

Однако я могу успешно импортировать содержимое, как только заменю все типы cq:Page и cq:PageContent на nt:unstructured. Это позволяет мне выполнять мой код в репозитории и читать содержимое, которое мой тестируемый класс записывает туда, но я немного обеспокоен тем, что тот факт, что мой тестовый контент имеет типы узлов, отличные от его реального аналога, может в некоторых случаях повлиять на результаты теста.

JCR_JACKRABBIT также требует создания базы данных Derby, которая не очищается между выполнениями тестов.

В общем, JCR_JACKRABBIT создает некоторые проблемы и не имеет никаких преимуществ перед JCR_MOCK в моем конкретном случае. Мне рекомендовали не использовать его, если мне действительно не нужно издеваться над расширенными функциями, не поддерживаемыми JCR_MOCK, такими как события или управление версиями.

Процитируем комментарий Стефана Зайферта в группе рассылки разработчиков wcm.io< /а>

используйте JCR_JACKRABBIT, только если вам нужны специальные функции, не поддерживаемые JCR_MOCK, например. управление событиями, управление версиями и т. д., но только в этом случае это самый тяжелый и самый неэффективный выбор, который в настоящее время мало используется. мы внесли небольшие улучшения в эту интеграцию в текущем стволе sling-mock-jackrabbit в sling, особенно. что касается загрузчика контента и специальных свойств, которые нельзя импортировать, но это еще не выпущено.

person toniedzwiedz    schedule 21.01.2015
comment
Я получаю сообщение об ошибке Не найдена фабрика адаптеров для сопоставления org.apache.sling.testing.resourceresolver.MockResourceResolver@5542c4ed с интерфейсом javax.jcr.Session, а сеанс равен null. Есть идеи? - person DerMike; 06.01.2016

Он говорит вам, что ваша реализация не знает об отдельном типе узла в вашей структуре и его ограничениях и не может создать такой узел.

Вы пытались поместить определение типа узла (nodetype.cnd) в свой путь к классам (просто поместите его в каталог src/test/resources) и загрузить его с помощью RepositoryUtil?

RepositoryUtil.registerNodeType(session, getClass().getResourceAsStream("nodetypes.cnd")); 
person Thomas    schedule 20.01.2015
comment
Спасибо, попробую. Возможно, реализация репозитория не знает о типах узлов, специфичных для AEM (например, cq:Page). Я возьму определения с dev.day.com, зарегистрирую их и посмотрю, решит ли это проблему. - person toniedzwiedz; 20.01.2015
comment
Я все еще немного борюсь с импортом, но после импорта типов узлов NullPointerException исчез. Теперь я столкнулся с PersistenceException, что звучит гораздо понятнее и, кажется, связано с деталями импорта типа узла. Я обновлю ответ, когда дойду до конца. - person toniedzwiedz; 20.01.2015
comment
Я связался с группой рассылки разработчиков wcm.io и узнал более простое решение проблемы, с которой я столкнулся, но ваше предложение очень помогло мне приблизиться к решению с использованием типа преобразователя JCR_JACKRABBIT resoruce. Я опубликовал и принял свой собственный ответ, который является более полным и описывает более широкую картину, но я с удовольствием проголосовал за ваш. Спасибо еще раз. - person toniedzwiedz; 21.01.2015