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! 🍻

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: