I have written below AEM servlet and junit5 test case but I am getting error while executing junit5 test case
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.servlets.annotations.SlingServletResourceTypes;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import javax.servlet.Servlet;
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.TimeUnit;
@Component(service = { Servlet.class })
@SlingServletResourceTypes(
resourceTypes = { "sample-aem/components/content/profile" },
methods = { "GET" },
extensions = { "json" })
@Designate(ocd = TestServlet.Configuration.class)
public class TestServlet extends SlingSafeMethodsServlet {
private Configuration configuration;
private PoolingHttpClientConnectionManager poolingHttpClientConnectionManager;
protected CloseableHttpClient closeableHttpClient;
@Activate
void activate(final Configuration config) {
this.configuration = config;
poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
poolingHttpClientConnectionManager.setMaxTotal(35);
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(2);
}
@Override
protected void doGet(SlingHttpServletRequest request,SlingHttpServletResponse response) throws IOException {
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
HttpGet httpGet = null;
URI uri = null;
String finalResponse = null;
try {
closeableHttpClient = HttpClients.custom().setConnectionManager(poolingHttpClientConnectionManager).build();
uri = new URIBuilder(configuration.baseUrl()+configuration.userid()).build();
httpGet = new HttpGet(uri);
finalResponse = getCloseableHttpClient().execute(httpGet, new CustomResponseHandler());
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (httpGet != null) {
httpGet.releaseConnection();
}
}
response.getWriter().write(finalResponse);
}
private CloseableHttpClient getCloseableHttpClient() {
this.poolingHttpClientConnectionManager.closeExpiredConnections();
this.poolingHttpClientConnectionManager.closeIdleConnections(5, TimeUnit.MINUTES);
return closeableHttpClient;
}
@ObjectClassDefinition
public @interface Configuration {
@AttributeDefinition(
name = "Base Url",
description = "API server base url"
)
String baseUrl() default "https://api.baseurl.com/";
@AttributeDefinition(
name = "userid",
description = "Unique user id."
)
String userid() default "12345";
}
}
Below is the helper class
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CustomResponseHandler implements ResponseHandler<String> {
@Override
public String handleResponse(HttpResponse response) throws IOException {
try {
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
if (statusCode == 201) {
String entityId = response.getFirstHeader("uniqueid").getValue();
Pattern pattern = Pattern.compile("\\((.*)\\)");
Matcher m = pattern.matcher(entityId);
if (m.find()) {
return m.group(1);
} else {
return entityId;
}
} else {
HttpEntity entity = response.getEntity();
String body = entity != null ? EntityUtils.toString(entity) : null;
return body;
}
} finally {
((CloseableHttpResponse) response).close();
}
}
}
I have written Sling Servlet junit5 testcase and added all required dependency in POM file and my junit5 test are working but below is the Junit5 test class throwing error while executing. Seems I have done something wrong while mocking the object
import io.wcm.testing.mock.aem.junit5.AemContext;
import io.wcm.testing.mock.aem.junit5.AemContextExtension;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicStatusLine;
import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletRequest;
import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletResponse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ExtendWith(AemContextExtension.class)
class ProfileServletTest {
AemContext aemContext = new AemContext();
ProfileServlet profileServlet = new ProfileServlet();
MockSlingHttpServletRequest request;
MockSlingHttpServletResponse response;
@Mock
private PoolingHttpClientConnectionManager cm;
@Mock
private CloseableHttpClient closeableHttpClient;
@Mock
CloseableHttpResponse closeableHttpResponse;
@Mock
private HttpClients httpClients;
@BeforeEach
void setUp() throws IOException {
aemContext.build().resource("/content/sample-aem/profile","jcr:title","Profile");
aemContext.currentResource("/content/sample-aem/profile");
aemContext.requestPathInfo().setExtension("json");
ProfileServlet.Configuration configuration = mock(ProfileServlet.Configuration.class);
when(configuration.baseUrl()).thenReturn("https://api.baseurl.com/");
when(configuration.userid()).thenReturn("12345");
when(httpClients.custom().setConnectionManager(cm).build()).thenReturn(closeableHttpClient);
when(closeableHttpClient.execute(any())).thenReturn(closeableHttpResponse);
when(closeableHttpResponse.getEntity()).thenReturn(new StringEntity("{'result':'ok'}"));
when(closeableHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("http",1,1),200,"OK"));
profileServlet.activate(configuration);
request = aemContext.request();
response = aemContext.response();
}
@Test
void doGet() throws IOException {
profileServlet.doGet(request,response);
assertEquals("{'result':'ok'}",response.getOutputAsString());
}
}
getting below error
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.
at aem.core.servlets.ProfileServletTest.setUp(ProfileServletTest.java:52)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:686)
The mocking of the http-client building had 2 main issues:
Here is a working version of your JUnit Test, with some comments:
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import io.wcm.testing.mock.aem.junit5.AemContext;
import io.wcm.testing.mock.aem.junit5.AemContextExtension;
import java.io.IOException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(AemContextExtension.class)
@ExtendWith(MockitoExtension.class)
class ProfileServletTest {
private final AemContext aemContext = new AemContext();
@Mock
private HttpClientBuilder httpClientBuilder;
@Mock
private CloseableHttpClient closeableHttpClient;
private ProfileServlet profileServlet;
@BeforeEach
void setUp() throws IOException {
aemContext.build().resource("/content/sample-aem/profile", "jcr:title", "Profile");
aemContext.currentResource("/content/sample-aem/profile");
aemContext.requestPathInfo().setExtension("json");
// Mock every step of the builder-chain
when(httpClientBuilder.setConnectionManager(any())).thenReturn(httpClientBuilder);
when(httpClientBuilder.build()).thenReturn(closeableHttpClient);
// execute() already returns a String
when(closeableHttpClient.execute(any(HttpGet.class), any(ResponseHandler.class)))
.thenReturn("{'result':'ok'}");
// use AEM-Mockups to register/inject/activate services
this.profileServlet = aemContext.registerInjectActivateService(ProfileServlet.class,
"baseUrl", "https://api.baseurl.com/", "userid", "12345");
}
@Test
void doGet() throws IOException {
// Static methods are NOT easy to mock
try (MockedStatic<HttpClients> httpClientsMock = Mockito.mockStatic(HttpClients.class)) {
httpClientsMock.when(HttpClients::custom).thenReturn(httpClientBuilder);
profileServlet.doGet(aemContext.request(), aemContext.response());
assertEquals("{'result':'ok'}", aemContext.response().getOutputAsString());
}
}
}