Expand my Community achievements bar.

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

how to do servlet unit testing

Avatar

Level 3

hi guys i need help from you, i created chillist servlet for reading properties from /content it returns a JSONArray of the node's child node paths and properties.

so plz help for unit testing. i need unit testing script for this code, this my code

package com.aem.community.core.servlets;

import java.io.IOException;

import javax.jcr.Node;

import javax.jcr.NodeIterator;

import javax.jcr.RepositoryException;

import javax.servlet.ServletException;

import org.apache.felix.scr.annotations.Service;

import org.apache.felix.scr.annotations.sling.SlingServlet;

import org.apache.sling.api.SlingHttpServletRequest;

import org.apache.sling.api.SlingHttpServletResponse;

import org.apache.sling.api.resource.Resource;

import org.apache.sling.api.resource.ResourceResolver;

import org.apache.sling.api.servlets.SlingAllMethodsServlet;

import org.apache.sling.commons.json.JSONArray;

import org.apache.sling.commons.json.JSONException;

import org.apache.sling.commons.json.JSONObject;

import org.apache.sling.commons.json.jcr.JsonJcrNode;

import org.osgi.service.component.annotations.Component;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

@Component(enabled = true, immediate = true)

@Service(ChildList.class)

@SlingServlet(resourceTypes="sling/servlet/default",selectors="childlist",methods="GET",extensions="json",metatype=true)

public class ChildList extends SlingAllMethodsServlet {

protected final Logger loger = LoggerFactory.getLogger(ChildList.class);

private static final long serialVersionUID = 9176255033916949528L;

private JSONArray array;

@Override

public void doGet(final SlingHttpServletRequest request, final SlingHttpServletResponse response)

throws ServletException, IOException {

try{

ResourceResolver resolver = request.getResourceResolver();

Resource resource = resolver.getResource(request.getRequestPathInfo().getResourcePath());

Node node = null;

if (resource != null) {

node = resource.adaptTo(Node.class);

}

if (node == null){

throw new RepositoryException();

}

NodeIterator it = node.getNodes();

array = new JSONArray();

while (it.hasNext()) {

Node child = it.nextNode();

if (loger.isDebugEnabled()){

loger.debug("resource......."+child.getPath());

}

JSONObject obj = new JsonJcrNode(child);

array.put(obj);

response.setContentType("application/json");

response.getOutputStream().print(array.toString());

}

}

catch(RepositoryException e) {

throw new ServletException("404 HTTP ERROR Page Not Found", e);

}

catch(JSONException e)

{

loger.error("Could not formulate JSON response");

throw new ServletException("Error", e);

}

}

}

1 Accepted Solution

Avatar

Correct answer by
Employee Advisor

Today I rewrote unittests for the whole day ... so while I just was on it, I wrote them for this question, too.

You can find the complete code at https://github.com/joerghoh/cqdump/tree/master/unittests

Clone it and then run it (the usual "mvn clean install").

The relevant bits and pieces:

I used the AEM Mocks from wcm.io, which work pretty good for usecases like yours.

HTH,

Jörg

View solution in original post

50 Replies

Avatar

Employee Advisor

Hi,

unittests ran as part of the build process, and you don't need to deploy the package to your local instance. If you want to do integration tests, then you need to deploy the code first to an AEM instance and run a testsuite against it.

In that case writing unittests for AEM is not any different than writing unittests for any other framework or technology. Just the frameworks helping you are different. But the general approach is 100% identical.

Jörg

Avatar

Level 3

this is our seervlet i want to make a service this servlet...???

package com.aem.community.core.servlets;

import java.io.IOException;

import javax.jcr.Node;

import javax.jcr.NodeIterator;

import javax.jcr.RepositoryException;

import javax.servlet.ServletException;

import org.apache.felix.scr.annotations.Service;

import org.apache.felix.scr.annotations.sling.SlingServlet;

import org.apache.sling.api.SlingHttpServletRequest;

import org.apache.sling.api.SlingHttpServletResponse;

import org.apache.sling.api.resource.Resource;

import org.apache.sling.api.resource.ResourceResolver;

import org.apache.sling.api.servlets.SlingAllMethodsServlet;

import org.apache.sling.commons.json.JSONArray;

import org.apache.sling.commons.json.JSONException;

import org.apache.sling.commons.json.JSONObject;

import org.apache.sling.commons.json.jcr.JsonJcrNode;

import org.osgi.service.component.annotations.Component;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

@Component(enabled = true, immediate = true)

@SlingServlet(resourceTypes="sling/servlet/default",selectors="childlist",methods="GET",ex tensions="json",metatype=true)

public class ChildList extends SlingAllMethodsServlet {

protected final Logger loger = LoggerFactory.getLogger(ChildList.class);

private static final long serialVersionUID = 9176255033916949528L;

private JSONArray array;

@Override

public void doGet(final SlingHttpServletRequest request, final SlingHttpServletResponse response)

throws ServletException, IOException {

try{

ResourceResolver resolver = request.getResourceResolver();

Resource resource = resolver.getResource(request.getRequestPathInfo().getResourcePath());

Node node = null;

if (resource != null) {

node = resource.adaptTo(Node.class);

}

if (node == null){

throw new RepositoryException();

}

NodeIterator it = node.getNodes();

array = new JSONArray();

while (it.hasNext()) {

Node child = it.nextNode();

if (loger.isDebugEnabled()){

loger.debug("resource......."+child.getPath());

}

JSONObject obj = new JsonJcrNode(child);

array.put(obj);

response.setContentType("application/json");

response.getOutputStream().print(array.toString());

}

}

catch(RepositoryException e) {

throw new ServletException("404 HTTP ERROR Page Not Found", e);

}

catch(JSONException e)

{

loger.error("Could not formulate JSON response");

throw new ServletException("Error", e);

}

}

}

Avatar

Level 10

A servelt is a service running in the OSGi service container. If you are using DS Annotations - there is no more @SlingServlet annotations, Everything uses @Component now. See - Official OSGi Declarative Services Annotations in AEM - Adobe Experience Manager | AEM/CQ | Apache S...

Avatar

Level 3

where we write this logic, how to write can u helpme

Create an HTML filtering filter as part of the publish tools bundle.

The filter can be disabled by the bundle configuration: enable.htmlfilter.filter {Boolean}

The filter must be disabled by default.

This filter will get involved in HTTP responses with a content-type of text/html.

The user will be able to "filter" the outputted HTML by optionally passing a querySelector (see https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector) in the request, and also, by passing an "extraction" method.

For example, let

/content/mysite/aboutus.html

output

<div class="about-us" title="Who we are">

    <h1>About Us</h1>

    <p>Find out who we are</p>

    We are the best.

</div>

In such example, the following htmlfilter parameters

/content/mysite/aboutus.html?htmlfilter={

    "filters": [{

           "selector": "h1",

           "extract": "TEXT"

    }]

}

Would replace the current response output with:

About Us

Usagection methods are supported

TEXTCONTENT

The following extra

OWNTEXT

INNERHTML

OUTERHTML

@attribute

The JSOUP library will be used to internal query and extract desired the HTML parts. (See https://jsoup.org/)

(Note that JSOUP is already provided in AEM when rapid-commons is installed)

In the example HTML page above and the following htmlfilter parameters

/content/mysite/aboutus.html?htmlfilter={

    "filters": [{

          "selector": ".about-us",

         "extract": "..."

    }]

}

If the extract property is set to TEXT the output would be

About UsFind out who we are We are the best.

If the `extract` property is set to `OWNTEXT` the output would be

We are the best.

If the `extract` property is set to `INNERHTML` the output would be

<h1>About Us</h1>

<p>Find out who we are</p>

We are the best.

If the `extract` property is set to `OUTERHTML` the output would be

<div class="about-us" title="Who we are">

<h1>About Us</h1>

<p>Find out who we are</p>

We are the best.

</div>

If the `extract` property is set to `@title` the output would be

Who we are

Multiple `filters` may also be passed. In such case a `glue` property may also be passed to the filter

/content/rapid/mysite/aboutus.html?htmlfilter={{{    }}

    "filters": [

        {

              "selector": "h1",

              "extract": "TEXT"

        },

        {

              "selector": "p",

              "extract": "TEXT"

        }

    ],

    "glue": ": "

}

Outputs

About Us: Find out who we are

Note that, the following htmlfilter parameters would have produced the same output for that page

/content/rapid/mysite/aboutus.html?htmlfilter={

    "filters": [{

            "selector": "h1, p",

            "extract": "TEXT"

    }],

    "glue": ": "

}

Outputs

About Us: Find out who we are

The advantage in the former format is that the user has the opportunity to pass a different `extract` value for each filter.

In the client does not pass a `glue` property or passed a `glue` property with a value `null`, the htmlfilter must use the default glue defined in the bundle setting:

htmlfilter.default.glue{String} = <i class="htmlfilter-glue"></i>

Thus such request:

/content/rapid/mysite/aboutus.html?htmlfilter={

    "filters": [{

          "selector": "h1, p",

          "extract": "TEXT"

    }]

}

Would output

About Us<i class="htmlfilter-glue"></i>Find out who we are

And such request:

/content/rapid/mysite/aboutus.html?htmlfilter={

    "filters": [

         {

               "selector": "h1",

               "extract": "TEXT"

         },

         {

               "selector": "h2",

               "extract": "TEXT"

         },

         {

               "selector": "p",

               "extract": "TEXT"

         }

    ]

}

Would output

About Us<i class="htmlfilter-glue"></i><i class="htmlfilter-glue"></i>Find out who we are

Parameters

The htmlfilter can take parameters in 2 different formats (but only 1 at a time):

As a request parameter (e.g. `request.getParameter()` provided either in the URL query string or POST form data

As a request header

Which one the filter is currently monitoring is is determined in the bundle configuration

htmlfilter.input {String} = parameter htmlfilter

The value of this configuration is simply a space delimited String in which the first part can either be `parameter` or `header` and the second part can be an arbitrary, space-less key.

For example, `parameter htmlfilter`, which is the default, means that the filter will get its input parameter in the request parameter named `htmlfilter`. (e.g. `request.getParamter("htmlfilter")`)

Had the value of this configuration be

htmlfilter.input{String}= header x-rpt-htmlfilter

It would mean that the filter will get its input parameter in the request header named `x-rpt-htmlfilter`. (e.g. `request.getHeader("x-rpt-htmlfilter")`)

The value if this configuration can easily be parsed with the following Regex Pattern

(parameter|header)[\s]([^\s])

Where the first captured group will be either `parameter` or `header` and the second captured group will be the key to look for.

Involvement

The filter must get involved in a request if and only if the following conditions are true

1. The configuation `enable.htmlfilter{Boolean}` is set to `true`

2. The request content type has the mime type "text/html" or "text/html+xml"

3. The either the `htmlfilter.input` request parameter or request header was provided in the request

Errors

If invalid parameters are passed to the htmlfilter (e.g. the "glue" property is passed as a number), the htmlfilter MUST change the current response status to an HTTP 400 Bad Request and log the error details in the server log.

Avatar

Level 3

public class ChildListJsonTest

{

@Rule

public AemContext context = new AemContext(ResourceResolverType.JCR_MOCK);

static final Logger LOG = LoggerFactory.getLogger(ChildListJsonTest.class);

@Before

public void setup() {

context.registerService(ImplementationPicker.class, new ResourceTypeBasedResourcePicker());

        context.addModelsForPackage("com.aem.community.core.servlets");

context.load().json(getClass().getResourceAsStream(getClass().getSimpleName() + ".json"),"/content");

}

@Test

public void testContentVersion() throws ServletException, IOException, JSONException {

ChildList servlet =new ChildList();

BundleContext bundleContext=MockOsgi.newBundleContext();

MockSling.setAdapterManagerBundleContext(bundleContext);

//bundleContext.registerService(myAdapterFactory);

//MockOsgi.injectServices(servlet, bundleContext);

context.currentPage(context.pageManager().getPage("/content"));   // getting path /content

context.currentResource(context.resourceResolver().getResource("/content")); // getting resource path /content

context.requestPathInfo().setResourcePath("/content");

servlet.doGet(context.request(), context.response());

assertTrue("Incorrect content type received","application/json".equals(context.response().getContentType())); //getting contentType application/json

LOG.info("output = {}", context.response().getOutputAsString());

JSONArray output = new JSONArray(context.response().getOutputAsString());

System.out.println("output"+output.length());

assertEquals("JSON Objects in array",25,output.length());

}

Capture2.PNG

how to solve this issue,i reffered BundleContextService  api but didn't get any  clarity about that

smacdonald2008Jörg Hohkautuksahnidgordon86

Avatar

Employee Advisor

Hi,

I am sorry, I got lost somewhere in this thread. For me it looks like that you are stuck with writing unit tests in an area which is not directly related to AEM anymore. I hope that I was able to provide you some starting point to write tests, but I am not able to support you writing unittests for your business logic. Although I am happy to help if there are questions how you can unit test (or mock) certain aspects of AEM.

kind regards,

Jörg

Avatar

Community Advisor

Hello @naveent23995593,

A more modern way to write Sling Servlets will be to write it with the OSGI DS 1.4 (R7) annotations. For instance, a Sling Servlet registered by resource types can utilise this annotation @SlingServletResourceType.

Writing unit tests for @SlingServletResourceType is very simple. Utilising the AEM Mocks JUnit library, First, you would need to instantiate the Sling Servlet object. Thereafter, you would need to pass in the Sling request and response (wcm.testing.io) mock objects to the doGet() method. Finally, you can start your assertion tests.

It makes more sense in this example (code snippet examples) - https://sourcedcode.com/aem-sling-servlet-osgi-r7-by-resource-type-unit-test-junit-4-with-examples