Skip to content

Unirest Mocks

About

A series of mocks for use with Unirest for unit testing. Mocked clients will not make any real web requests. This allows you to test the input into unirest and to mock responses from expected requests.

    flowchart TD
        A[Your Code] --> B(Unirest Pubilc Interface)
        B --> C{Core Implimentation}
        C -->|When Mocked| D[Mocked Client]
        C -->|Normal Runtime| F[Java HttpClient]

Expecting Requests

You can either mock the default static implementation or a per instance implementation. In both cases you need to register the mock with Unirest.

Static Mocking

class MyTest {
    @Test
    void mockStatic(){
        MockClient mock = MockClient.register();

        mock.expect(HttpMethod.GET, "http://zombo.com")
                        .thenReturn("You can do anything!");

        assertEquals(
            "You can do anything!", 
            Unirest.get("http://zombo.com").asString().getBody()
        );
    }
}

Instant Mocking

    @Test
    void mockInstant(){
        UnirestInstance unirest = Unirest.spawnInstance();
        MockClient mock = MockClient.register(unirest);

        mock.expect(HttpMethod.GET, "http://zombo.com")
                        .thenReturn("You can do anything!");

        assertEquals(
            "You can do anything!", 
            unirest.get("http://zombo.com").asString().getBody()
        );
    }

Multiple Expects

HTTP requests can have many parts, some of which are automatic or at least uninteresting from the standpoint of testing. This means that setting up an exact expectation to match the request exactly can be tedious.

You can register as many expects as you like. Which one is used for any particular invocation of Unirest depends on a points system. Each expectation is evaluated and given points for each positive part while any negative part immediately discards the expect. The expectation that has the most points "wins".

In this example, we have three expectations, one doesn't match at all. and two others match but one does more than the other so the most specific match is used.

    @Test
    void multipleExpects(){
        MockClient mock = MockClient.register();

        mock.expect(HttpMethod.POST, "https://somewhere.bad")
                .thenReturn("I'm Bad");

        mock.expect(HttpMethod.GET, "http://zombo.com")
                .thenReturn("You can do anything!");

        mock.expect(HttpMethod.GET, "http://zombo.com")
                .header("foo", "bar")
                .thenReturn("You can do anything with headers!");

        assertEquals(
                "You can do anything with headers!",
                Unirest.get("http://zombo.com")
                        .header("foo", "bar")
                        .asString().getBody()
        );

        assertEquals(
                "You can do anything!",
                Unirest.get("http://zombo.com")
                        .asString().getBody()
        );
    }

Verifying Expects

Sometimes we only want to know that the needful was done. In this case we can validate our mock. The simplest way is to call verifyAll which will validate that all expects were called at least once.

    @Test
    void verifyAll(){
        MockClient mock = MockClient.register();

        mock.expect(HttpMethod.POST, "http://zombo.com")
                .thenReturn().withStatus(200);

        Unirest.post("http://zombo.com").asString().getBody();

        mock.verifyAll();
    }

If you want to get more specific we can keep around our expectations and validate them explicitly. We can also inject a number of times we want to validate (including zero)

    @Test
    void verifyMultiple(){
        MockClient mock = MockClient.register();

        var zombo =    mock.expect(HttpMethod.POST, "http://zombo.com").thenReturn();
        var homestar = mock.expect(HttpMethod.DELETE, "http://homestarrunner.com").thenReturn();

        Unirest.post("http://zombo.com").asString().getBody();

        zombo.verify();
        homestar.verify(Times.never());
    }

Expected Body Matching

You can match specific body content with some limitations. Complex bodies must implement BodyMatcher. There are two implementations available: EqualsBodyMatcher which is used for simple equality and FieldMatcher which is for form params. You can create your own.

Simple Bodies

    @Test
    void simpleBody() {
        MockClient mock = MockClient.register();

        mock.expect(HttpMethod.POST, "http://zombo.com")
                .body("I can do anything? Anything at all?")
                .thenReturn()
                .withStatus(201);

        assertEquals(201,
                Unirest.post("http://zombo.com").body("I can do anything? Anything at all?").asEmpty().getStatus()
        );
    }

Form Params

    @Test
    void formParams() {
        MockClient mock = MockClient.register();

        mock.expect(HttpMethod.POST, "http://zombo.com")
                .body(FieldMatcher.of("foo", "bar",
                                      "baz", "qux"))
                .thenReturn()
                .withStatus(201);

        assertEquals(201,
                Unirest.post("http://zombo.com")
                        .field("foo", "bar")
                        .field("baz", "qux")
                        .asEmpty().getStatus()
        );
    }

Expected Responses

You can set all properties of a response.

    @Test
    void response() {
        MockClient mock = MockClient.register();

        mock.expect(HttpMethod.GET, "http://zombo.com")
                .thenReturn("Care for some tea mum?")
                .withHeader("x-zombo-brewing", "active")
                .withStatus(418, "I am a teapot");

        var response = Unirest.get("http://zombo.com").asString();

        assertEquals(418, response.getStatus());
        assertEquals("I am a teapot", response.getStatusText());
        assertEquals("Care for some tea mum?", response.getBody());
        assertEquals("active", response.getHeaders().getFirst("x-zombo-brewing"));
    }

Responses with JSON Bodies

The mocking framework will use whatever ObjectMapper is configured with Unirest to marshall Pojos to expected responses.

    static class Teapot { public String brewstatus = "on"; }
    @Test
    void pojos() {
        MockClient mock = MockClient.register();

        mock.expect(HttpMethod.GET, "http://zombo.com")
                .thenReturn(new Teapot());

        var response = Unirest.get("http://zombo.com").asString();

        assertEquals("{\"brewstatus\":\"on\"}", response.getBody());
    }