net.jadler.JadlerStubbingIntegrationTest.java Source code

Java tutorial

Introduction

Here is the source code for net.jadler.JadlerStubbingIntegrationTest.java

Source

/*
 * Copyright (c) 2013 Jadler contributors
 * This program is made available under the terms of the MIT License.
 */
package net.jadler;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.io.IOUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.net.Socket;
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;

import static net.jadler.Jadler.closeJadler;
import static net.jadler.Jadler.initJadler;
import static net.jadler.Jadler.onRequest;
import static net.jadler.Jadler.port;
import static net.jadler.Jadler.recordedRequests;
import static net.jadler.matchers.MethodRequestMatcher.requestMethod;
import static net.jadler.matchers.QueryStringRequestMatcher.requestQueryString;
import static org.hamcrest.Matchers.anything;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.everyItem;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.isEmptyOrNullString;
import static org.hamcrest.Matchers.isEmptyString;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;

/**
 * Suite of several integration tests for the stubbing part of the Jadler library.
 * Each test configures the stub server and tests either the <i>WHEN<i/> or <i>THEN</i> part of http stubbing using
 * an http client.
 */
public class JadlerStubbingIntegrationTest {

    private static final int DEFAULT_STATUS = 409;
    private static final String DEFAULT_HEADER1_NAME = "default_header";
    private static final String DEFAULT_HEADER1_VALUE1 = "value1";
    private static final String DEFAULT_HEADER1_VALUE2 = "value2";

    private static final String STRING_WITH_DIACRITICS = "\u00e1\u0159\u017e";
    private static final byte[] UTF_8_REPRESENTATION = { (byte) 0xC3, (byte) 0xA1, (byte) 0xC5, (byte) 0x99,
            (byte) 0xC5, (byte) 0xBE };
    private static final byte[] ISO_8859_2_REPRESENTATION = { (byte) 0xE1, (byte) 0xF8, (byte) 0xBE };

    private static final byte[] BINARY_BODY = { 1, 2, 3 };

    private static final String UTF_8_TYPE = "text/html; charset=UTF-8";
    private static final Charset UTF_8_CHARSET = Charset.forName("UTF-8");
    private static final String ISO_8859_2_TYPE = "text/html; charset=ISO-8859-2";
    private static final Charset ISO_8859_2_CHARSET = Charset.forName("ISO-8859-2");

    private HttpClient client;

    @Before
    public void setUp() {

        initJadler().that().respondsWithDefaultStatus(DEFAULT_STATUS)
                .respondsWithDefaultHeader(DEFAULT_HEADER1_NAME, DEFAULT_HEADER1_VALUE1)
                .respondsWithDefaultHeader(DEFAULT_HEADER1_NAME, DEFAULT_HEADER1_VALUE2)
                .respondsWithDefaultEncoding(UTF_8_CHARSET).respondsWithDefaultContentType(UTF_8_TYPE);

        this.client = new HttpClient();
    }

    @After
    public void tearDown() {
        closeJadler();
    }

    /*
     * Tests the <tt>havingBody</tt> methods.
     */
    @Test
    public void havingBody() throws Exception {
        onRequest().havingBodyEqualTo("postbody").havingBody(notNullValue()).havingBody(not(isEmptyOrNullString()))
                .respond().withStatus(201);

        final PostMethod method = new PostMethod("http://localhost:" + port());
        method.setRequestEntity(new StringRequestEntity("postbody", null, null));

        int status = client.executeMethod(method);
        assertThat(status, is(201));
    }

    /*
     * An empty string (not null) is matched for an empty http request.
     */
    @Test
    public void havingEmptyBody() throws Exception {
        onRequest().havingBodyEqualTo("").havingBody(notNullValue()).havingBody(isEmptyString()).respond()
                .withStatus(201);

        final PostMethod method = new PostMethod("http://localhost:" + port());
        method.setRequestEntity(new StringRequestEntity("", null, null));

        int status = client.executeMethod(method);
        assertThat(status, is(201));
    }

    /*
     * Tests the <tt>havingRawBody</tt> method
     */
    @Test
    public void havingRawBody() throws IOException {
        onRequest().havingRawBodyEqualTo(BINARY_BODY).respond().withStatus(201);

        final PostMethod method = new PostMethod("http://localhost:" + port());
        method.setRequestEntity(new ByteArrayRequestEntity(BINARY_BODY));

        final int status = client.executeMethod(method);
        assertThat(status, is(201));
    }

    /*
     * An empty array (not null) is matched for an empty http request.
     */
    @Test
    public void havingRawEmptyBody() throws IOException {
        onRequest().havingRawBodyEqualTo(new byte[] {}).respond().withStatus(201);

        final PostMethod method = new PostMethod("http://localhost:" + port());

        final int status = client.executeMethod(method);
        assertThat(status, is(201));
    }

    /*
     * Matches a request with a text body encoded using UTF-8.
     */
    @Test
    public void havingUTF8Body() throws Exception {

        onRequest().havingBodyEqualTo(STRING_WITH_DIACRITICS).respond().withStatus(201);

        final PostMethod method = new PostMethod("http://localhost:" + port());
        method.setRequestEntity(
                new StringRequestEntity(STRING_WITH_DIACRITICS, "text/plain", UTF_8_CHARSET.name()));

        int status = client.executeMethod(method);
        assertThat(status, is(201));
    }

    /*
     * Matches a request with a text body encoded using ISO-8859-2.
     */
    @Test
    public void havingISOBody() throws Exception {

        onRequest().havingBodyEqualTo(STRING_WITH_DIACRITICS).respond().withStatus(201);

        final PostMethod method = new PostMethod("http://localhost:" + port());
        method.setRequestEntity(
                new StringRequestEntity(STRING_WITH_DIACRITICS, "text/plain", ISO_8859_2_CHARSET.name()));

        int status = client.executeMethod(method);
        assertThat(status, is(201));
    }

    /*
     * Tests a combination of the <tt>havingHeader</tt> methods.
     */
    @Test
    public void havingHeader() throws Exception {

        onRequest().havingHeader("hdr1").havingHeader("hdr1", hasSize(1))
                .havingHeader("hdr1", everyItem(not(isEmptyOrNullString()))).havingHeader("hdr1", contains("h1v1"))
                .havingHeaderEqualTo("hdr1", "h1v1").havingHeader("hdr2").havingHeader("hdr2", hasSize(2))
                .havingHeader("hdr2", contains("h2v1", "h2v2")).havingHeader("hdr3", nullValue())
                .havingHeaderEqualTo("hdr2", "h2v1").havingHeaderEqualTo("hdr2", "h2v2")
                .havingHeaders("hdr1", "hdr2").respond().withStatus(201);

        final GetMethod method = new GetMethod("http://localhost:" + port());
        method.addRequestHeader("hdr1", "h1v1");
        method.addRequestHeader("hdr2", "h2v1");
        method.addRequestHeader("hdr2", "h2v2");

        int status = client.executeMethod(method);
        assertThat(status, is(201));

        assertThat(recordedRequests(), hasItem(requestMethod(equalTo("GET"))));
        assertThat(recordedRequests(), hasSize(1));
    }

    /*
     * Tests the <tt>havingMethod</tt> methods.
     */
    @Test
    public void havingMethod() throws Exception {
        onRequest().havingMethodEqualTo("POST")
                //the comparison must be case insensitive
                .havingMethodEqualTo("poSt").havingMethod(not(isEmptyOrNullString())).havingMethod(anything())
                .respond().withStatus(201);

        final PostMethod method = new PostMethod("http://localhost:" + port());

        int status = client.executeMethod(method);
        assertThat(status, is(201));

        //assertThat(recordedRequests(), hasItem(requestQueryString(equalTo("localhost"))));
        assertThat(recordedRequests(), hasSize(1));
    }

    /*
     * Tests the <tt>havingParameter</tt> methods for a GET http request. Only query string values
     * are considered http parameters for a GET http request.
     */
    @Test
    public void havingParameterGET() throws Exception {

        onRequest().havingParameter("p1").havingParameter("p1", hasSize(1))
                .havingParameter("p1", everyItem(not(isEmptyOrNullString())))
                .havingParameter("p1", contains("p1v1")).havingParameterEqualTo("p1", "p1v1").havingParameter("p2")
                .havingParameter("p2", hasSize(2)).havingParameter("p2", hasItems("p2v1", "p2v2"))
                .havingParameterEqualTo("p2", "p2v1").havingParameterEqualTo("p2", "p2v2")
                .havingParameters("p1", "p2").havingParameter("p3").havingParameter("p3", contains(""))
                .havingParameterEqualTo("p3", "").havingParameter("p4").havingParameter("p4", contains(""))
                .havingParameterEqualTo("p4", "").havingParameter("p5", nullValue()).respond().withStatus(201);

        final GetMethod method = new GetMethod("http://localhost:" + port() + "?p1=p1v1&p2=p2v1&p2=p2v2&p3=&p4");

        int status = client.executeMethod(method);
        assertThat(status, is(201));
    }

    /*
     * Tests the <tt>havingParameter</tt> methods for a POST http request with the
     * <tt>application/x-www-form-urlencoded</tt> content type. Both query string and request body values
     * are considered http parameters for such an http request.
     * 
     * This test also tests a combination of <tt>havingParameter</tt> and <tt>havingBody</tt> methods
     * since both of these require an access to the request body (which causes troubles in the servlet specification
     * addressed by the {@link MultipleReadsHttpServletRequest} wrapper).
     */
    @Test
    public void havingParameterPOST() throws Exception {
        final String body = "p1=p1v1&p2=p2v1&p2=p2v2&p3=&p4";

        onRequest().havingParameter("p1").havingParameter("p1", hasSize(1))
                .havingParameter("p1", everyItem(not(isEmptyOrNullString())))
                .havingParameter("p1", contains("p1v1")).havingParameterEqualTo("p1", "p1v1").havingParameter("p2")
                .havingParameter("p2", hasSize(3)).havingParameter("p2", hasItems("p2v1", "p2v2", "p2v3"))
                .havingParameterEqualTo("p2", "p2v1").havingParameterEqualTo("p2", "p2v2")
                .havingParameterEqualTo("p2", "p2v3").havingParameters("p1", "p2").havingParameter("p3")
                .havingParameter("p3", contains("")).havingParameterEqualTo("p3", "").havingParameter("p4")
                .havingParameter("p4", contains("")).havingParameterEqualTo("p4", "")
                .havingParameter("p5", nullValue()).havingBodyEqualTo(body).respond().withStatus(201);

        final PostMethod method = new PostMethod("http://localhost:" + port() + "?p2=p2v3");
        method.setRequestEntity(new StringRequestEntity(body, "application/x-www-form-urlencoded", "UTF-8"));

        int status = client.executeMethod(method);
        assertThat(status, is(201));
    }

    /*
     * Tests the <tt>havingQueryString</tt> methods. 
     */
    @Test
    public void havingQueryString() throws Exception {
        onRequest().havingQueryStringEqualTo("p1=v1&p2=v2").havingQueryString(not(isEmptyOrNullString()))
                .havingQueryString(anything()).respond().withStatus(201);

        final GetMethod method = new GetMethod("http://localhost:" + port() + "?p1=v1&p2=v2");

        int status = client.executeMethod(method);
        assertThat(status, is(201));
    }

    /*
     * Null value is matched for an empty http request.
     */
    @Test
    public void havingNoQueryString() throws Exception {
        onRequest().havingQueryString(nullValue()).havingQueryString(not(equalTo(""))).respond().withStatus(201);

        final GetMethod method = new GetMethod("http://localhost:" + port());

        int status = client.executeMethod(method);
        assertThat(status, is(201));
    }

    /*
     * Tests the <tt>havingURI</tt> methods.
     */
    @Test
    public void havingURI() throws Exception {
        onRequest().havingURIEqualTo("/a/b/c/d").havingURI(notNullValue()).respond().withStatus(201);

        final GetMethod method = new GetMethod("http://localhost:" + port() + "/a/b/c/d");

        int status = client.executeMethod(method);
        assertThat(status, is(201));
    }

    /*
     * Tests the <tt>havingURI</tt> methods for a root URI.
     */
    @Test
    public void havingRootURI() throws IOException {
        onRequest().havingURI(equalTo("/")).havingURI(not(isEmptyOrNullString())).respond().withStatus(201);

        //if there was no slash at the end, the GetMethod constructor would add it automatically
        final GetMethod method = new GetMethod("http://localhost:" + port() + "/");

        final int status = client.executeMethod(method);
        assertThat(status, is(201));
    }

    @Test
    @Ignore
    public void havingURISockets() throws IOException {
        onRequest().havingURIEqualTo("/").respond().withStatus(201);

        final Socket sock = new Socket("localhost", port());
        final OutputStream out = sock.getOutputStream();
        out.write("GET / HTTP/1.1\r\nHost:localhost\r\n\r\n".getBytes());

        InputStream in = sock.getInputStream();

        int b;
        while ((b = in.read()) != -1) {
            char res = (char) b;
            System.out.print(res);
        }
    }

    /*
     * Tests the <tt>withBody(String)</tt> method in connection with the default body encoding (UTF-8).
     * Retrieves the response body as an array of bytes and checks that it's exactly the same as
     * the UTF-8 representation of the string.
     */
    @Test
    public void withDefaultEncoding() throws IOException {
        onRequest().respond().withBody(STRING_WITH_DIACRITICS);

        final GetMethod method = new GetMethod("http://localhost:" + port());

        client.executeMethod(method);

        //check that the body was really encoded in UTF-8
        final byte[] body = IOUtils.toByteArray(method.getResponseBodyAsStream());
        assertThat(body, is(UTF_8_REPRESENTATION));
    }

    /*
     * Tests the <tt>withBody(String)</tt> method in connection with the <tt>withEncoding</tt> method.
     * Retrieves the response body as an array of bytes and checks that it's exactly the same as
     * the ISO-8859-2 representation of the string.
     */
    @Test
    public void withEncoding() throws IOException {
        onRequest().respond().withEncoding(ISO_8859_2_CHARSET).withBody(STRING_WITH_DIACRITICS);

        final GetMethod method = new GetMethod("http://localhost:" + port());

        client.executeMethod(method);

        //check that the body was really encoded in UTF-8
        final byte[] body = IOUtils.toByteArray(method.getResponseBodyAsStream());
        assertThat(body, is(ISO_8859_2_REPRESENTATION));
    }

    /*
     * Tests the <tt>withBody(String)</tt> method in connection with the default content type
     * (which is set to text/html; charset=UTF-8). Checks the content type was set to the stub response
     * and the body is readable to the http client.
     */
    @Test
    public void withDefaultContentType() throws IOException {
        onRequest().respond().withBody(STRING_WITH_DIACRITICS);

        final GetMethod method = new GetMethod("http://localhost:" + port());

        client.executeMethod(method);

        //the content type header set to the default value
        assertThat(method.getResponseHeader("Content-Type").getValue(), is(UTF_8_TYPE));

        //the http client was able to retrieve the charset portion of the content type header
        assertThat(method.getResponseCharSet(), is(UTF_8_CHARSET.name()));

        //since the body was encoded in UTF-8 and content type charset was set to UTF-8,
        //the http client should be able to read it correctly
        assertThat(method.getResponseBodyAsString(), is(STRING_WITH_DIACRITICS));
    }

    /*
     * Tests the <tt>withBody(String)</tt> method in connection with the <tt>withEncoding</tt> and
     * <tt>withContentType</tt> methods. Both body encoding and the content type header are set
     * to ISO-8859-2. Checks the content type was set to the stub response and the body is readable to the http client.
     */
    @Test
    public void withContentType() throws IOException {
        onRequest().respond().withEncoding(ISO_8859_2_CHARSET).withContentType(ISO_8859_2_TYPE)
                .withBody(STRING_WITH_DIACRITICS);

        final GetMethod method = new GetMethod("http://localhost:" + port());

        client.executeMethod(method);

        //the content type header set to the specified value
        assertThat(method.getResponseHeader("Content-Type").getValue(), is(ISO_8859_2_TYPE));

        //the http client was able to retrieve the charset portion of the content type header
        assertThat(method.getResponseCharSet(), is(ISO_8859_2_CHARSET.name()));

        //since the body was encoded in "ISO-8859-2" and content type charset was set to "ISO-8859-2",
        //the client should be able to read it correctly
        assertThat(method.getResponseBodyAsString(), is(STRING_WITH_DIACRITICS));
    }

    /*
     * Tests a mismatch between body encoding and the encoding stated in the content type header.
     * Body is encoded using ISO-8859-2, however the content type header states it's encoded
     * using UTF-8.
     */
    @Test
    public void withContentTypeEncodingMismatch() throws IOException {
        onRequest().respond().withEncoding(ISO_8859_2_CHARSET).withContentType(UTF_8_TYPE)
                .withBody(STRING_WITH_DIACRITICS);

        final GetMethod method = new GetMethod("http://localhost:" + port());

        client.executeMethod(method);

        //the content type header set to the specified value
        assertThat(method.getResponseHeader("Content-Type").getValue(), is(UTF_8_TYPE));

        //the http client was able to retrieve the charset portion of the content type header
        assertThat(method.getResponseCharSet(), is(UTF_8_CHARSET.name()));

        //however the applied encoding is ISO-8859-2
        final byte[] body = IOUtils.toByteArray(method.getResponseBodyAsStream());
        assertThat(body, is(ISO_8859_2_REPRESENTATION));
    }

    /*
     * Tests the <tt>withBody(Reader)</tt> method. The reader content is encoded using the ISO-8859-2 encoding
     * and then retrieved.
     */
    @Test
    public void withBodyReader() throws IOException {
        final Reader r = new StringReader(STRING_WITH_DIACRITICS);

        onRequest().respond().withBody(r).withEncoding(ISO_8859_2_CHARSET).withContentType(ISO_8859_2_TYPE);

        final GetMethod method = new GetMethod("http://localhost:" + port());
        client.executeMethod(method);

        final byte[] resultBody = IOUtils.toByteArray(method.getResponseBodyAsStream());
        assertThat(resultBody, is(ISO_8859_2_REPRESENTATION));
    }

    /*
     * Tests the <tt>withBody(InputStream)</tt> method. The stream content is used straight as the response body
     * and then retrieved.
     */
    @Test
    public void withBodyInputStream() throws IOException {
        final InputStream is = new ByteArrayInputStream(BINARY_BODY);

        onRequest().respond().withBody(is);

        final GetMethod method = new GetMethod("http://localhost:" + port());
        client.executeMethod(method);

        final byte[] resultBody = IOUtils.toByteArray(method.getResponseBodyAsStream());
        assertThat(resultBody, is(BINARY_BODY));
    }

    /*
     * Tests the <tt>withBody(byte[])</tt> method. The byte array is used straight as the response body
     * and then retrieved.
     */
    @Test
    public void withBodyArrayOfBytes() throws IOException {
        onRequest().respond().withBody(BINARY_BODY);

        final GetMethod method = new GetMethod("http://localhost:" + port());
        client.executeMethod(method);

        final byte[] resultBody = IOUtils.toByteArray(method.getResponseBodyAsStream());
        assertThat(resultBody, is(BINARY_BODY));
    }

    /*
     * Tests that for more matching stub rules the latter is applied.
     */
    @Test
    public void rulesOrdering() throws IOException {
        //these 3 rules are always matched, the latter one must be applied
        onRequest().that(is(anything())).respond().withStatus(201);
        onRequest().that(is(anything())).respond().withStatus(202);
        onRequest().that(is(anything())).respond().withStatus(203);

        final GetMethod method = new GetMethod("http://localhost:" + port());
        final int status = client.executeMethod(method);
        assertThat(status, is(203));
    }

    /*
     * Tests that 500 status and an empty body is returned when no stub rule matches.
     */
    @Test
    public void noRuleApplicable() throws IOException {
        onRequest().that(is(not(anything()))).respond();
        final GetMethod method = new GetMethod("http://localhost:" + port());
        final int status = client.executeMethod(method);

        assertThat(status, is(404));
        assertThat(method.getResponseHeader("Content-Type").getValue(), is("text/plain; charset=utf-8"));
        assertThat(method.getResponseBodyAsString(), is("No stub response found for the incoming request"));
    }

    /*
     * Tests default status and response headers 
     */
    @Test
    public void defaults() throws Exception {
        onRequest().respond();

        final GetMethod method = new GetMethod("http://localhost:" + port());
        int status = client.executeMethod(method);
        assertThat(status, is(DEFAULT_STATUS));

        final Header[] responseHeaders = method.getResponseHeaders(DEFAULT_HEADER1_NAME);
        assertThat(responseHeaders.length, is(2));
        assertThat(responseHeaders[0].getName(), is(DEFAULT_HEADER1_NAME));
        assertThat(responseHeaders[0].getValue(), is(DEFAULT_HEADER1_VALUE1));

        assertThat(responseHeaders[1].getName(), is(DEFAULT_HEADER1_NAME));
        assertThat(responseHeaders[1].getValue(), is(DEFAULT_HEADER1_VALUE2));
    }

    /*
     * Tests overriding the default status during stubbing
     */
    @Test
    public void overridenDefaultStatus() throws Exception {
        onRequest().respond().withStatus(201);

        final GetMethod method = new GetMethod("http://localhost:" + port());
        int status = client.executeMethod(method);
        assertThat(status, is(201));
    }

    /*
     * Tests overriding default headers. The header DEFAULT_HEADER1_NAME is already defined with two default values.
     * This particular stubbing adds a third value. This test checks that all three values
     * are sent in the stub response.
     */
    @Test
    public void overridenDefaultHeader() throws Exception {
        onRequest().respond().withHeader(DEFAULT_HEADER1_NAME, "value3");

        final GetMethod method = new GetMethod("http://localhost:" + port());
        int status = client.executeMethod(method);
        assertThat(status, is(DEFAULT_STATUS));

        final Header[] responseHeaders = method.getResponseHeaders(DEFAULT_HEADER1_NAME);
        assertThat(responseHeaders.length, is(3));
        assertThat(responseHeaders[0].getName(), is(DEFAULT_HEADER1_NAME));
        assertThat(responseHeaders[0].getValue(), is(DEFAULT_HEADER1_VALUE1));

        assertThat(responseHeaders[1].getName(), is(DEFAULT_HEADER1_NAME));
        assertThat(responseHeaders[1].getValue(), is(DEFAULT_HEADER1_VALUE2));

        assertThat(responseHeaders[2].getName(), is(DEFAULT_HEADER1_NAME));
        assertThat(responseHeaders[2].getValue(), is("value3"));
    }

    /*
     * Tests the stub response is returned after at least three seconds as set during the stubbing
     */
    @Test
    public void timeout() throws IOException {
        onRequest().respond().withTimeout(3, TimeUnit.SECONDS);

        final GetMethod method = new GetMethod("http://localhost:" + port());

        final long start = System.currentTimeMillis();
        client.executeMethod(method);
        final long end = System.currentTimeMillis();
        final long dur = end - start;
        assertThat(dur / 1000, is(greaterThanOrEqualTo(3L)));
    }
}