Expand my Community achievements bar.

Dive into Adobe Summit 2024! Explore curated list of AEM sessions & labs, register, connect with experts, ask questions, engage, and share insights. Don't miss the excitement.
SOLVED

How to Adapt ContentFragment in tests

Avatar

Level 3

Hello.

Could you please tell me how to properly adapt the Resource to the ContentFragment in the tests. Since I do, I get null all the time, but the resource content fragment is not null. And in the class for which the test is written, adaptation works correctly.

 

1.jpg2.jpg

 

 

 

1 Accepted Solution

Avatar

Correct answer by
Level 10

Hello @aliaksandr_hvoz,

The issue is likely with your JSON content. In order for the AEMMock resource resolver to correctly identify the your resource as a content fragment, it must satisfy some basic elements of what a content fragment is. This is the same for any JSON-based test resource.

One thing that you maybe overlooked is the presence of the metadata child which is necessary to instantiate a ContentFragment. Here is a working example:

JSON:

{
    "dam": {
        "content-fragment": {
            "jcr:primaryType": "dam:Asset",
            "jcr:content": {
                "jcr:primaryType": "dam:AssetContent",
                "jcr:title": "Lorem Ipsum 1",
                "contentFragment": true,
                "cq:name": "lorem-ipsum-1",
                "metadata": {
                    "jcr:primaryType": "nt:unstructured"
                }
            }
        }
    }
}

Java:

import com.adobe.cq.dam.cfm.ContentFragment;
import io.wcm.testing.mock.aem.junit5.AemContext;
import io.wcm.testing.mock.aem.junit5.AemContextExtension;
import org.apache.sling.api.resource.Resource;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith(AemContextExtension.class)
class ContentFragmentTest {

    private final AemContext context = new AemContext();

    @BeforeEach
    public void setUp() throws Exception {
        context.load().json("/test.json", "/content");
    }

    @Test
    public void testSomething() {
        final Resource resource = context.resourceResolver().getResource("/content/dam/content-fragment");
        final ContentFragment contentFragment = resource.adaptTo(ContentFragment.class);
        Assertions.assertNotNull(contentFragment);
    }

}

 If you add these files to your project and run the test, you will be able to adapt the /content/dam/content-fragment to a ContentFragment and therefore pass the test 

Selection_157.png

However, if you then delete the metadata node from the test JSON, the test will fail:

Selection_158.png

Without seeing your JSON, I can't guarantee that this is the problem, but it seems likely 

If you don't know what the minimum requirements for a test JSON resource are, I recommend:

  • Finding an example on your AEM instance (eg: I used /content/dam/tipi/content-fragments/lorem-ipsum-1 on my instance)
  • Using the JSON exporter to get some valid JSON (eg: visit /content/dam/tipi/content-fragments/lorem-ipsum-1.infinity.json in your browser)
  • Remove the unneeded nodes and properties until you arrive at the minimum, essential properties (usually that's the jcr:primaryType, the sling:resourceType, sling:resourceSuperType, that kind of thing)

Note: You can also see in my JSON that the dam node has no properties. That's because it's purely a structural node used to create a realistic path.

Hope that helps!

View solution in original post

7 Replies

Avatar

Community Advisor

Hi @aliaksandr_hvoz 

 

Your code seems to be correct. However, you can go through this link https://docs.adobe.com/content/help/en/experience-manager-64/developing/extending-aem/customizing-co...

 

Also, Could you please confirm which type of resource resolver you are using?

Are you creating resource resolver via service user? If yes, could you please verify whether the corresponding service user has the read access to the content fragment and service mapping is present in OSGI. (refer this http://www.aemcq5tutorials.com/tutorials/create-system-user-in-aem/)

 

Regards,

Arpit 

Avatar

Level 3
I use AemContext and load JSON with content fragment template and I use resource resolver from AemContext this.context.resourceResolver()

Avatar

Correct answer by
Level 10

Hello @aliaksandr_hvoz,

The issue is likely with your JSON content. In order for the AEMMock resource resolver to correctly identify the your resource as a content fragment, it must satisfy some basic elements of what a content fragment is. This is the same for any JSON-based test resource.

One thing that you maybe overlooked is the presence of the metadata child which is necessary to instantiate a ContentFragment. Here is a working example:

JSON:

{
    "dam": {
        "content-fragment": {
            "jcr:primaryType": "dam:Asset",
            "jcr:content": {
                "jcr:primaryType": "dam:AssetContent",
                "jcr:title": "Lorem Ipsum 1",
                "contentFragment": true,
                "cq:name": "lorem-ipsum-1",
                "metadata": {
                    "jcr:primaryType": "nt:unstructured"
                }
            }
        }
    }
}

Java:

import com.adobe.cq.dam.cfm.ContentFragment;
import io.wcm.testing.mock.aem.junit5.AemContext;
import io.wcm.testing.mock.aem.junit5.AemContextExtension;
import org.apache.sling.api.resource.Resource;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith(AemContextExtension.class)
class ContentFragmentTest {

    private final AemContext context = new AemContext();

    @BeforeEach
    public void setUp() throws Exception {
        context.load().json("/test.json", "/content");
    }

    @Test
    public void testSomething() {
        final Resource resource = context.resourceResolver().getResource("/content/dam/content-fragment");
        final ContentFragment contentFragment = resource.adaptTo(ContentFragment.class);
        Assertions.assertNotNull(contentFragment);
    }

}

 If you add these files to your project and run the test, you will be able to adapt the /content/dam/content-fragment to a ContentFragment and therefore pass the test 

Selection_157.png

However, if you then delete the metadata node from the test JSON, the test will fail:

Selection_158.png

Without seeing your JSON, I can't guarantee that this is the problem, but it seems likely 

If you don't know what the minimum requirements for a test JSON resource are, I recommend:

  • Finding an example on your AEM instance (eg: I used /content/dam/tipi/content-fragments/lorem-ipsum-1 on my instance)
  • Using the JSON exporter to get some valid JSON (eg: visit /content/dam/tipi/content-fragments/lorem-ipsum-1.infinity.json in your browser)
  • Remove the unneeded nodes and properties until you arrive at the minimum, essential properties (usually that's the jcr:primaryType, the sling:resourceType, sling:resourceSuperType, that kind of thing)

Note: You can also see in my JSON that the dam node has no properties. That's because it's purely a structural node used to create a realistic path.

Hope that helps!

Avatar

Level 3

Yes, it works, but one point - it works if you use JUnit 5 (io.wcm.testing.aem-mock.junit5). I have a lot of tests on JUnit 4 and I can not migrate from JUnit 4 to JUnit 5.

 

Avatar

Level 2

@Theo_Pendle and @LokeshTripathi 

 

I was facing the same issue and followed your suggestions mentioned, it worked for me

Thanks Theo and Lokesh.

Avatar

Level 1

I have got Content Fragment mock object as mentioned by @Theo_Pendle , but I got a problem at next level.

My problem was -: I was not able to get the variations from the contefragment's mocked resource which I had set from AEM context. I was loading ContentFragment in the AEM context like this in my test class.

 

 

(1) context.load().json(getClass().getResourceAsStream(CF_JSON_PATH), CF_PATH);

 

 

I was expecting that doing above will adapt to the ContentFragment Object properly and we will get everything inside ContentFragment object when below code runs into the class.

 

(2) ContentFragment contentFragment = resourceResolver.getResource(CF_PATH).adaptTo(ContentFragment.class);

 

 

Loading  ContentFragment via Json [as mentioned at (1)] was giving Mocked ContentFragment Object, but when we were trying to access the various getter methods on ContentFragment object they were coming null.

For example I was not able to get list of Variations into my Junit for the code

(3) Iterator<VariationDef> variationIterator = contentFragment.listAllVariations();


Above problem got resolved when I replaced this line in my Junit

context.load().json(getClass().getResourceAsStream(CF_JSON_PATH), CF_PATH);

 

 

with this piece of code 

 

ContentFragment cf = context.create().contentFragmentStructured(CF_PATH, "main", "Hello There");
cf.createVariation("variation1", "variation1 name", "variation1 description");
cf.createVariation("variation2", "variation2 name"", "variation2 description");

So basically I am not using Json to load the Mock content fragment into my Junit. With this approach we can set variations, elements etc at Content Fragment.

Reference

https://wcm.io/testing/aem-mock/junit5/apidocs/io/wcm/testing/mock/aem/builder/ContentBuilder.html

https://wcm-io.atlassian.net/wiki/spaces/WCMIO/blog/2019/08/19/1217069057/What+s+new+in+AEM+Mocks+2....

https://stackoverflow.com/questions/53124973/mocking-aem-asset-manager-using-wcm-io

 

Hope it helps people if they are stuck at similar point. 

Happy coding.