Print
Category: Software Development
Hits: 2808

It is pretty easy to simulate a web service using jetty. And with this example it will be even easier to create an integration test.

Note: This article was written assuming that you are already familiar with Maven, Log4j and JUnit.

The first thing we have to do is to add the Jetty dependency to our pom.xml file:

<dependency>
              <groupId>org.mortbay.jetty</groupId>
              <artifactId>jetty-servlet-tester</artifactId>
              <version>6.1.16</version>
              <scope>test</scope>
 </dependency>

It is important that to set the scope of the dependency to test because we usually don't want to use this library in the production server.
The next step is to create a server class which it is going to receive the request and send back the response. This class is going to extend from the GenericServlet and we have to code only the service method. In the above example, the servlet gets the account number and looks in a map for the name of the file it has to return. Then it opens the file and copies its contents to the response.
By using a map and text files, it is very easy to add a new test case if that is required. The test files are kept in the src/test/resources directory.

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.log4j.Logger;
 
public class AccountServiceServlet extends GenericServlet {
    private static final Logger logger = Logger
            .getLogger(AccountServiceServlet.class);
    private static final long serialVersionUID = 1L;
    private Map<String, String> testAccountNumbers;
 
    public AccountServiceServlet() {
        this.getTestAccountNumbers().put("50386102", "/testFiles/50386102/AccountService_response01.xml");
        this.getTestAccountNumbers().put("50166121", "/testFiles/50166121/AccountService_response.xml");
        this.getTestAccountNumbers().put("00484119", "/testFiles/00484119/AccountService_response.xml");
    }
 
    @Override
    public void service(ServletRequest request, ServletResponse response)
            throws ServletException, IOException {
        BufferedReader contentReader = request.getReader();
        StringBuilder stringBuilder = new StringBuilder();
        String lineSep = System.getProperty("line.separator");
 
        String nextLine;
        while ((nextLine = contentReader.readLine()) != null) {
            stringBuilder.append(nextLine);
            stringBuilder.append(lineSep);
        }
 
        String content = stringBuilder.toString();
        logger.info("Received request: " + content);
 
        Pattern pattern = Pattern.compile("AccountNumber>(\\S+)</AccountNumber>");
        Matcher aMatcher = pattern.matcher(content);
        if (aMatcher.find()) {
            String accountNumber = aMatcher.group(1);
            logger.debug("Getting response file for account #" + accountNumber);
            String testFileName = this.getTestAccountNumbers().get(accountNumber);
            logger.debug("Test File: " + testFileName);
            if (testFileName != null) {
                URL fileLocation = this.getClass().getResource(testFileName);
                if (fileLocation == null)
                    throw new ServletException("Unable to find '" + testFileName + "'");
 
                BufferedReader fileResponseReader;
                try {
                    fileResponseReader = new BufferedReader(new FileReader(new File(fileLocation.toURI())));
                } catch (URISyntaxException e) {
                    throw new ServletException("Syntax error converting to an URI: " + fileLocation);
                }
                while ((nextLine = fileResponseReader.readLine()) != null) {
                    response.getWriter().append(nextLine);
                    response.getWriter().append(lineSep);
                }
                return;
            }
        }
 
        if(response instanceof HttpServletResponse){
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            httpResponse.setContentType("text/xml");
            httpResponse.setCharacterEncoding("UTF-8");
            httpResponse.setContentLength(0);
        } else {
            throw new ServletException("The account doesn't exist.");
        }
    }
 
 
    public void setTestAccountNumbers(Map<String, String> testAccountNumbers) {
        this.testAccountNumbers = testAccountNumbers;
    }
 
 
    public Map<String, String> getTestAccountNumbers() {
        if (testAccountNumbers == null)
            testAccountNumbers = new HashMap<String, String>();
        return testAccountNumbers;
    }
}

Now we have to create a test which uses this servlet. There are a few methods we have to code here:

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Iterator;
 
import junit.framework.Assert;
 
import org.apache.log4j.Logger;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mortbay.jetty.testing.ServletTester;
 
public class TestAcccountServiceImplementation {
    private static final Logger logger = Logger
            .getLogger(TestAcccountServiceImplementation.class);
    private static ServletTester testServlet;
    private static final String SERVLET_ACCOUNT_SERVICE_PATH = "/QA/services/AccountService";
 
    @BeforeClass
    public static void initServletContainer () throws Exception
    {
        testServlet = new ServletTester();
        testServlet.setContextPath("/");
        testServlet.addServlet(AccountServiceServlet.class, SERVLET_ACCOUNT_SERVICE_PATH);
 
        String servletBaseUrl = testServlet.createSocketConnector(false);
        String accountSeviceServletBaseUrl = servletBaseUrl + SERVLET_ACCOUNT_SERVICE_PATH;
        logger.info("Base URL of the account service servlet: " + accountSeviceServletBaseUrl);
        MockPropertiesConfiguration.setWebserviceAccountServiceBaseUrl(accountSeviceServletBaseUrl);
        testServlet.start();
    }
    @AfterClass
    public static void cleanupServletContainer () throws Exception
    {
        testServlet.stop();
    }
 
    @Test
    public void testFindAccount() throws ProgramFault {
        AccountService accountService = Context.getAccoutService();
        Account anAccount;
        AmericanAccount anAmericanAccount;
 
        logger.info("Testing account #50386102");
        anAccount = accountService.getBrokerAccount("50386102");
        Assert.assertNotNull(anAccount);
        Assert.assertTrue(anAccount instanceof CachedTermsAmericanAccount);
        anAmericanAccount = (AmericanAccount) anAccount;
        Assert.assertEquals("50386102", anAmericanAccount.getNumber());
        Assert.assertEquals(3, anAmericanAccount.getTermsCount());
        //More tests.
        logger.debug("End of find account test.");
    }
 
    @Test(expected=ProgramFault.class)
    public void testConnectionError() throws ProgramFault {
        logger.info("Testing connection error");
        String oldDatabaseLookupUrl = MockPropertiesConfiguration.getWebserviceAccountServiceBaseUrl();
        MockPropertiesConfiguration.setWebserviceAccountServiceBaseUrl("http://127.0.0.1:9999" + SERVLET_ACCOUNT_SERVICE_PATH);
        try {
            Context.getAccoutService().getCustomerAccount("999");
        } finally {
            MockPropertiesConfiguration.setWebserviceDatabaseLookupBaseUrl(oldDatabaseLookupUrl);
        }
    }
 }
 

I changed a little bit the original code to reduce its size. In case you found any error, just let me know.