I am using Mockito with JUnit to test an application. I need to add headers to an HttpServletRequest
while mocking. This is the first time I am using mock concept to test the application. How can we set headers to the request object while using this mock concept?
@Produces({ MediaType.APPLICATION_JSON })
@Path("/devices")
public class DvrRestService {
private static final Logger logger = LoggerFactory.getLogger(DvrRestService.class);
private DvrMiddleService dvrMiddleService;
@Inject
public DvrRestService(DvrMiddleService dvrMiddleService) {
this.dvrMiddleService = dvrMiddleService;
}
@GET
@Path("/{deviceId}/metadata")
public Response getDeviceMetadata(@Context HttpServletRequest request, @PathParam("deviceId") String deviceId,
@RequiredSession final Session session) {
try {
public static String[] REQUEST_HEADERS = { "if-none-match" };
List<String> requiredHeaders = Lists.newArrayList(REQUEST_HEADERS);
Map<String, String> headers = new HashMap<String, String>();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) { // here gettting NullPointerException
String headerName = headerNames.nextElement();
if (requiredHeaders.contains(headerName.toLowerCase())) {
String value = request.getHeader(headerName);
if (value != null) {
headers.put(headerName, value);
System.out.println("headerName: " + headerName + ", Value: " + value);
}
}
}
DvrResponse response = dvrMiddleService.getDeviceMetadata(deviceId.toUpperCase(), getHeaders(request));
return processResponse(response.statusCode, response.getResponse(), DeviceMetadataResponse.class,
response.getHeaders());
} catch (Exception e) {
return processErrorResponse(e, new DeviceMetadataResponse(), logger);
}
}
}
public class DvrRestServiceTest {
static DvrMiddleService dms;
static HttpServletRequest request;
static Session session;
static DvrRestService drs;
public static final String DeviceId = "000004D42070";
@BeforeClass
public static void init(){
dms = mock(DvrMiddleService.class);
request = mock(HttpServletRequest.class);
session = mock(Session.class);
drs = new DvrRestService(dms);
}
@Test
public void getDeviceMetadataTest(){
Response rs = drs.getDeviceMetadata(request, DeviceId, session);
assertEquals(Response.Status.OK, rs.getStatus());
}
}
MockHttpServletRequest request = new MockHttpServletRequest(); request. addHeader("x-real-ip","127.0. 0.1");
public class MockHttpServletRequest extends Object implements HttpServletRequest. Mock implementation of the HttpServletRequest interface. The default, preferred Locale for the server mocked by this request is Locale. ENGLISH . This value can be changed via addPreferredLocale(java.
As a starting point and demonstration for the principal you can start with the following snippet.
// define the headers you want to be returned
Map<String, String> headers = new HashMap<>();
headers.put(null, "HTTP/1.1 200 OK");
headers.put("Content-Type", "text/html");
// create an Enumeration over the header keys
Enumeration<String> headerNames = Collections.enumeration(headers.keySet());
// mock HttpServletRequest
HttpServletRequest request = mock(HttpServletRequest.class);
// mock the returned value of request.getHeaderNames()
when(request.getHeaderNames()).thenReturn(headerNames);
System.out.println("demonstrate output of request.getHeaderNames()");
while (headerNames.hasMoreElements()) {
System.out.println("header name: " + headerNames.nextElement());
}
// mock the returned value of request.getHeader(String name)
doAnswer(new Answer<String>() {
@Override
public String answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
return headers.get((String) args[0]);
}
}).when(request).getHeader("Content-Type");
System.out.println("demonstrate output of request.getHeader(String name)");
String headerName = "Content-Type";
System.out.printf("header name: [%s] value: [%s]%n",
headerName, request.getHeader(headerName));
}
demonstrate output of request.getHeaderNames()
header name: null
header name: Content-Type
demonstrate output of request.getHeader(String name)
header name: [Content-Type] value: [text/html]
For HttpServletRequest
, I would recommend using a fully functional mock type instead of directly mocking it with Mockito mocks. The spring-test
library has MockHttpServletRequest
for this purpose:
@BeforeClass
public static void init(){
// ...
MockHttpServletRequest mockRequest = new MockHttpServletRequest();
mockRequest.addHeader("Content-Type", "text/html");
mockRequest.addHeader("if-none-match", "*");
mockRequest.addHeader("customHeader", "customValue");
this.request = mockRequest;
}
HttpServletRequest
is a complex interface with over 20 methods, with well-defined interplay between them. Using a fully functional mock type for HttpServletRequest
from a library simplifies the mocking, removing the need to carefully mock out the methods you're using.
One advantage of this approach is that it is more resilient in the face of future refactorings that get the same information using other methods on the class. In the case of retrieving the "if-none-match" header in HttpServletRequest
, I see three different methods that could legitimately be used to retrieve the header: getHeader(String name)
, getHeaders(String name)
, and getHeaderNames()
. Furthermore, the argument for both getHeader
and getHeaders
are case-insensitive (the same results are returned for "if-none-match", "If-None-Match", etc.), so any possible argument casing would be correct. It's very possible to support this with a direct mock, though it involves extra boilerplate code that complicates the test and makes it less obvious.
The MockHttpServletRequest
class from the spring-test
library mocks this interface, and allows setting the header and other values via a straightforward API. While the library is designed for testing Spring applications, the MockHttpServletRequest
class is independent of any Spring-specific functionality, and should be completely usable on its own even if the application doesn't use Spring.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With