JUnit Test in AEM 6.5 – OSGi service

How to test an OSGi service with AEMContext in AEM

Goal

Create unit test class for an OSGi service. We will extend the Generic Abstract Class created here,
In this post we are going to test the RestClient class available here.

Procedure

We will test 3 different scenarios. First, let’s create our test class as follows:

package com.adobe.training.core.service;

import com.adobe.training.core.models.AbstractBaseTest;
import com.adobe.training.core.service.impl.RestClientImpl;
import junitx.util.PrivateAccessor;
import org.apache.http.HttpEntity;
import org.apache.http.StatusLine;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.sling.api.resource.Resource;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.junit.MockitoJUnitRunner;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class RestClientImplTest extends AbstractBaseTest {

    @InjectMocks
    private RestClientImpl restClient;

    private final AtomicReference<CloseableHttpClient> httpClient = new AtomicReference<>();

    private final AtomicReference<HttpClientContext> clientContext = new AtomicReference<>();

    private final String PATH = "/v1/path";

    public final static String LIST_RESULT = "{\"resultCount\":1," + "\"entities\":[" + "{" +
            "\"_id\":\"175a1935-b4b6-49a1-b568-16dec9677f9a\"" + "}" + "]" + "}";

    public static final String REST_CLIENT_CONFIGURATION_CONFIG_PATH = "/apps/Lab2020/config/com.adobe.training.core.service.general.impl.RestClientImpl.config";


    @Before
    public void setUp() throws NoSuchFieldException {
        super.setUp();
    }

    @Override
    protected void loadCommonContent() {
        aemContext.load().json("/osgiConfig/restClientConfiguration.json", REST_CLIENT_CONFIGURATION_CONFIG_PATH);
    }


    @Test
    public void activate() throws NoSuchFieldException {
        Resource configsResource = resourceResolver.getResource(REST_CLIENT_CONFIGURATION_CONFIG_PATH);
        aemContext.registerInjectActivateService(restClient, configsResource.getValueMap());
        AtomicReference<RequestConfig> requestConfig = (AtomicReference<RequestConfig>) PrivateAccessor.getField(restClient, "requestConfig");
        RequestConfig requestConfigObject = requestConfig.get();
        assertEquals("https://baseUri", PrivateAccessor.getField(restClient, "baseUri"));
        assertEquals(5000, requestConfigObject.getConnectTimeout());
        assertEquals(30000, requestConfigObject.getSocketTimeout());
    }


    @Test
    public void executeOk() throws IOException, URISyntaxException, NoSuchFieldException {
        Map<String, String> parameters = new HashMap<>();
        parameters.put("param1", "value1");
        parameters.put("param2", "value2");
        Map<String, String> headers = new HashMap<>();
        headers.put("header1", "header");
        PrivateAccessor.setField(restClient, "baseUri", "https://baseUri");
        HttpClientContext httpClientContext = mock(HttpClientContext.class);
        clientContext.set(httpClientContext);
        CloseableHttpClient closeableHttpClient = mock(CloseableHttpClient.class);
        CloseableHttpResponse closeableHttpResponse = mock(CloseableHttpResponse.class);
        StatusLine statusLine = mock(StatusLine.class);
        setUpClient(closeableHttpClient, closeableHttpResponse, statusLine);
        when(statusLine.getStatusCode()).thenReturn(200);
        JSONObject execute = restClient.execute(PATH, parameters, headers);
        assertEquals(LIST_RESULT, execute.toString());
    }


    @Test
    public void executeKo() throws IOException, URISyntaxException, NoSuchFieldException {
        Map<String, String> parameters = new HashMap<>();
        parameters.put("param1", "value1");
        parameters.put("param2", "value2");
        Map<String, String> headers = new HashMap<>();
        headers.put("header1", "header");
        PrivateAccessor.setField(restClient, "baseUri", "https://baseUri");
        HttpClientContext httpClientContext = mock(HttpClientContext.class);
        clientContext.set(httpClientContext);
        CloseableHttpClient closeableHttpClient = mock(CloseableHttpClient.class);
        CloseableHttpResponse closeableHttpResponse = mock(CloseableHttpResponse.class);
        StatusLine statusLine = mock(StatusLine.class);
        setUpClient(closeableHttpClient, closeableHttpResponse, statusLine);
        when(statusLine.getStatusCode()).thenReturn(500);
        JSONObject execute = restClient.execute(PATH, parameters, headers);
        assertEquals("{}", execute.toString());
    }


    private void setUpClient(CloseableHttpClient closeableHttpClient, CloseableHttpResponse closeableHttpResponse, StatusLine statusLine) throws NoSuchFieldException, IOException {
        httpClient.set(closeableHttpClient);
        PrivateAccessor.setField(restClient, "httpClient", httpClient);
        PrivateAccessor.setField(restClient, "clientContext", clientContext);
        when(closeableHttpClient.execute(any(HttpGet.class), any(HttpClientContext.class))).thenReturn(closeableHttpResponse);
        when(closeableHttpResponse.getStatusLine()).thenReturn(statusLine);
        HttpEntity httpEntity = mock(HttpEntity.class);
        when(closeableHttpResponse.getEntity()).thenReturn(httpEntity);
        InputStream is = new ByteArrayInputStream(LIST_RESULT.getBytes());
        when(httpEntity.getContent()).thenReturn(is);
    }
}

The first test, the activate() method, is to cover the OSGi activate method. You need to create this OSGI configuration file:

{
  "jcr:primaryType": "sling:OsgiConfig",
  "getEndpoint":"https://baseUri",
  "getConnectTimeout": 5000,
  "getReadTimeout":30000,
  "getPoolSize":10,
  "getListOfHandledErrors":[504],
  "getNumberOfRetries":10,
  "getRetryInterval":5
}

It will be loaded at line 58 and then these configurations will be retrieved at lines 64-65.

The others two tests are for the execute method, and they cover both a response OK and KO using the when/then methodology. You can add more logic and assertions based on different parameters and headers for instance.
The code should be readable, therefore I didn’t add any other explanation, but if you need something drop a comment! For other Junit Test examples, check them out here.

Cheers! 🍻

4 thoughts on “JUnit Test in AEM 6.5 – OSGi service

  1. Hi, good noon,

    Can you help me find JUNIT-5 code for the above API-service code which you have written here. I mean for RestClientImpl. I am looking for it.
    Thank you

    Like

Leave a comment