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