Tuesday, 5 July 2016

Mocking Http Server in Java for Integration Testing

If you want to create a mock Http server for integration testing to mock external URLS/Servers. Here is the solution for java.

I have gone through the all options like WireMock, nanoHttpd,Mock-Server,LightWeightServer…etc but not full fill my requirements and not easy to use for my use case.

I created my own solution for this problem and of course inspired from other blogs to get into this .

First use  Dependencies:

testCompile group: 'org.mortbay.jetty', name: 'jetty', version: '7.0.0.pre5'
compile 'commons-io:commons-io:2.5'

Mortbay Jetty will do simple and nice job here.

If you’re working with webserver clients you will certainly have noticed the difficulty in integration-testing your webserver clients. Building webserver clients can already be quite a complex task, but providing a mock-up webservice backend delivering useful test responses is quite often just to much work, if not impossible, since many web server backends are very complex constructs. Furthermore, setting up a backend for each integration test consumes valuable build time.
As an alternative, one may simply replace the backend with a mock HTTP server that does one thing: deliver the expected webserver response – i.e. XML data – when called by the client. All you need to set this up is a HTTP server and a pre-recorded service response (quite often this comes with WS client specifications). If you don’t have this data, you can record it, for example using a proxy server in between your WS client and a real WS backend, or a tool such as wireshark.

The recorded XML response thus represents your assumptions against which you test the client. This makes your tests fast, lightweight and a lot better to understand.

Server Code Here

package com.java.testing.httpclient;

import static javax.servlet.http.HttpServletResponse.SC_OK;
import static org.apache.commons.io.IOUtils.write;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.http.entity.ContentType;
import org.junit.Ignore;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.HttpConnection;
import org.mortbay.jetty.Request;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.AbstractHandler;

import com.touchpeak.restclient.util.NullUtil;

/**
 * Used for Mocking Http Server
 * 
 * @author Kumar
 *
 */
@Ignore
public class MockHttpTestServer {

 private int HTTP_PORT = 55556;
 private Server _server;
 private String _responseBody;
 private String _requestBody;
 private String _mockResponseData;
 private Integer _mockResponseStatus;
 private String _mockResponseContentType;
 private String _requestPath;
 private String _requestMethod;
 private Map requestedQueryParams = new HashMap<>();

 public MockHttpTestServer() {
 }

 public MockHttpTestServer(int port) {
  HTTP_PORT = port;
 }

 public MockHttpTestServer(String mockData) {
  setMockResponseData(mockData);
 }

 public void start() throws Exception {
  configureServer();
  startServer();
 }

 private void startServer() throws Exception {
  _server.start();
 }

 protected void configureServer() {
  _server = new Server(HTTP_PORT);
  _server.setHandler(getMockHandler());
 }

 /**
  * Creates an {@link AbstractHandler handler} returning an arbitrary String
  * as a response.
  * 
  * @return never null.
  */
 public Handler getMockHandler() {
  Handler handler = new AbstractHandler() {

   public void handle(String target, HttpServletRequest request,
     HttpServletResponse response, int dispatch)
     throws IOException, ServletException {

    cleanInMemory();
    Request baseRequest = request instanceof Request ? (Request) request
      : HttpConnection.getCurrentConnection().getRequest();
    setRequestMethod(baseRequest.getMethod());
    setRequestPath(baseRequest.getUri().getPath());
    setResponseBody(getMockResponseData());
    setRequestBody(IOUtils.toString(baseRequest.getInputStream(),
      "UTF-8"));
    response.setStatus(getMockResponseStatus());
    response.setContentType(getMockResponseContentType() != null ? getMockResponseContentType()
      : "text/plain");
    write(getResponseBody(), response.getOutputStream(), "UTF-8");
    baseRequest.setHandled(true);
   }
  };
  return handler;
 }

 protected void cleanInMemory() {

  setRequestBody("");
  setRequestPath("");

 }

 public void stop() throws Exception {
  _server.stop();
 }

 public void setResponseBody(String responseBody) {
  _responseBody = responseBody;
 }

 public String getResponseBody() {
  return _responseBody;
 }

 public void setRequestBody(String requestBody) {
  _requestBody = requestBody;
 }

 public String getRequestBody() {
  return _requestBody;
 }

 public static void main(String[] args) {
  MockHttpTestServer server = new MockHttpTestServer();
  try {
   server.start();
  } catch (Exception e) {
   throw new RuntimeException(e);
  }
 }

 public void setMockResponseData(String mockResponseData) {
  _mockResponseData = mockResponseData;
 }

 public String getMockResponseData() {
  return _mockResponseData;
 }

 protected Server getServer() {
  return _server;
 }

 public Map getRequestedQueryParams() {
  return requestedQueryParams;
 }

 public String getRequestPath() {
  return _requestPath;
 }

 public String getMockResponseContentType() {
  return _mockResponseContentType;
 }

 public void setMockResponseContentType(String _mockResponseContentType) {
  this._mockResponseContentType = _mockResponseContentType;
 }

 public Integer getMockResponseStatus() {
  return NullUtil.isNullOrEmpty(_mockResponseStatus) ? SC_OK
    : _mockResponseStatus;
 }

 public void setMockResponseStatus(Integer _mockResponseStatus) {
  this._mockResponseStatus = _mockResponseStatus;
 }

 public void setRequestPath(String _requestPath) {
  this._requestPath = _requestPath;
 }

 public String getRequestMethod() {
  return _requestMethod;
 }

 public void setRequestMethod(String _requestMethod) {
  this._requestMethod = _requestMethod;
 }
}

Client Code Here
package com.java.testing.httpclient;

import static org.junit.Assert.assertEquals;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.touchpeak.restclient.test.MockHttpTestServer;

public class HttpClientTests {

 
 private static final Logger logger = LoggerFactory
   .getLogger(HttpClientTests.class);
 
 private MockHttpTestServer server;

 private String HOST = "127.0.0.1";
 private int PORT =56342;
 private String TEST_BASE_URI = "http://"+HOST+":"+PORT;
 
 @Before
 public  void init() 
 {
  server = new MockHttpTestServer(PORT);
  try {
   server.start();
  } catch (Exception e) {
   logger.error(" While starting Mock Http Server ",e);
  }
 }
 
 
 @After
 public void close() 
 {
  try {
   server.stop();
  } catch (Exception e) {
   logger.error(" While stopping Mock Http Server ",e);
  }
 }

 
 @Test
 public void test_Server_Call() 
 {
  String mockResponse = "Mock Response you expect from server";
  String mockRequestBody ="RequestBody Sent to server from client"; 
  server.setMockResponseData(mockResponse);
  
  //Server will return above expected responses to client call.
  String actualResponse = " write client code here to call server and get the response from server call";
  
  assertEquals("GET", server.getRequestMethod());//Check HTTP METHOD here
  assertEquals("/order/3?area=LONDON", server.getRequestPath());// Check Request URI
  assertEquals(mockRequestBody, server.getRequestBody());//Check RequestBody received by Server
  assertEquals(mockResponse, actualResponse);//check Response from server
  
  
 }

}
Tips: 1. you can also add Listeners to Server class to get control back on client side while executing.

No comments:

Post a Comment

Please comment here