badge

Recently, I explained a fluent Java HTTP client created (mostly) to make HTTP interactions more object-oriented than with other available clients,including: Apache Client, Jersey Client and plain old HttpURLConnection.

This client ships in the jcabi-http Maven artifact. However, the client part is not the only benefit of using jcabi-http. Jcabi also includes a server component that can help you in unit and integration testing of your HTTP clients.

Let me show you an example first. In the example, I'm using hamcrest for assertions.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
MkContainer container = new MkGrizzlyContainer()
  .next(new MkAnswer.Simple("hello, world!"))
  .start();
try {
  new JdkRequest(container.home())
    .header("User-agent", "Myself")
    .fetch()
    .assertBody(Matchers.containsString("hello"));
} finally {
  container.stop();
}
MkQuery query = container.take();
MatcherAssert.assertThat(
  query.headers().get("User-agent"),
  Matchers.hasItem("Myself")
);

Now, let's discover what happens here.

In the first few lines, I create an instance of MkContainer, which literally has four methods: next(MkAnswer), start(), stop(), and home().

It works as an HTTP server with a "first-in-first-out" queue for HTTP answers. We add answers, and the server returns them in response to HTTP requests.

The server starts on start() call and stops on stop(). Its method home() returns a URL of its "home page". The server then binds itself to a randomly allocated TCP port.

The container finds the first available and unoccupied port.

In the example above, I added just one answer. This means that the container will reply only to the first HTTP request with that answer and that all consecutive requests will cause HTTP responses with status "internal server error 500."

In lines 5 through 8, I make an HTTP request to the already started server. Also, I make an assertion that the body of the HTTP response contains the text "hello". Obviously, this assertion will pass because the server will return "hello, world!" to my first request:

new JdkRequest(container.home())
  .header("User-agent", "Myself")
  .fetch()
  .assertBody(Matchers.containsString("hello"));

As you can see, I use container.home() in order to get the URL of the server. It is recommended that you allow the container to find the first unoccupied TCP port and bind itself to it. Nevertheless, if you need to specify your own port, you can do it with a one-argument method start(int) in MkContainer.

I use try/finally to stop the container safely. In unit tests, this is not critical, as you can simplify your code and never stop the container. Besides, the container will be killed together with the JVM. However, for the sake of clarity, I would recommend you stop the container in the finally block.

On line 12, I ask the stopped container to give me the first request it received. This mechanism is similar conceptually to the "verify" technology of mocking frameworks. For example, Mockito.

MkQuery query = container.take();
MatcherAssert.assertThat(
  query.headers().get("User-agent"),
  Matchers.hasItem("Myself")
);

An instance of MkQuery exposes information about the query made. In this example, I get all headers of the HTTP request and making an assertion that the"User-Agent" header was there and had at least one value equal to "Myself".

This mocking technology is used actively in unit and integration tests of jcabi-github, which is a Java client to Github API. In its development, the technology is very important in checking which requests are being sent to the server and validating whether they comply with our requirements. Here, we are using jcabi-http mocking.

As with the client, you need the jcabi-http.jar dependency (get its latest versions in Maven Central):

<dependency>
  <groupId>com.jcabi</groupId>
  <artifactId>jcabi-http</artifactId>
</dependency>

Besides the above, you need to add one more dependency, which is a Grizzly HTTP server. MkGrizzlyContainer is based on it.

<dependency>
  <groupId>com.sun.grizzly</groupId>
  <artifactId>grizzly-servlet-webserver</artifactId>
  <scope>test</scope>
</dependency>

If you have any questions or suggestions, please submit them through Github issues. As always, bugs are welcome :)