avatarLuís Soares

Summary

The website content describes how to test a REST client in Kotlin/Java using Javalin to simulate an external REST API for both query (GET) and command (POST) operations, ensuring the gateway client consumes the API correctly.

Abstract

The article discusses testing strategies for a REST client in a Kotlin/Java application, emphasizing the use of Javalin as a lightweight web server to simulate external REST APIs during tests. The author outlines two examples: testing a GET request to fetch user profiles and testing a POST request to assert the proper posting of data. The Javalin server acts as a test double, providing a local environment that mimics the real API's behavior without network latency. The author provides a general recipe for testing, which includes arranging Javalin with appropriate handlers, acting by calling the subject method, and asserting the correct parsing and transformation of data or the proper execution of side effects. The article also contrasts this approach with alternative methods such as mocking the HTTP client, using a dedicated library with a DSL, or conducting integration and system testing, ultimately advocating for Javalin due to its simplicity and realism in simulating network conditions.

Opinions

  • The author believes that using Javalin for testing REST clients is superior to mocking HTTP clients or their wrappers, as it avoids coupling tests to implementation details and abstracts the external party's data model.
  • Javalin is praised for its fast startup time, which allows for numerous starts and stops without significantly slowing down tests.
  • The author advises against assertions inside Javalin test handlers to prevent JUnit exceptions from being swallowed by Javalin, which could result in falsely passing tests.
  • Mocking what you don't own, such as a database driver or a REST client, is considered a bad practice by the author.
  • The author has personal experience with alternatives like MockServer, WireMock, and JSON Server but prefers Javalin for its ease of use and debugging capabilities.
  • The article suggests that Javalin testing is complementary to other testing methods like integration, system, contract, and end-to-end testing, rather than a replacement.

Testing a gateway client using Javalin

In Kotlin/Java, how would you test that your app is properly consuming an external REST API? We’ll use Javalin to do it.

How do you unit test a gateway in your app?

📝 I adapted this article to the official Javalin docs page.

My proposal is to use Javalin (a lightweight webserver) as the test double, thereby replacing the external API (the DOC: dependent-on component). We’ll launch Javalin acting as the real API but running in localhost so that the gateway (the SUT) can’t tell the difference. We’ll confirm the validity by asserting the calls made to the test double.

⚠️ Don’t confuse testing API calls with testing your own API handlers. We want to tackle the former: testing a REST client; not a REST service.

Before starting

We’ll have two examples of testing ProfileGateway: a query and a command, according to the command/query separation:

  • query: check that it properly consumes a GET response from an external party; we’ll assert the output of a method;
  • command: check that a POST call was made as expected; we’ll assert a consequence, namely the posted body.

We just need this boilerplate that guarantees that Javalin stops per every test:

Don’t worry as this won’t make your tests slow; Javalin is extremely fast booting up (hundreds of starts/stops in a few seconds).

📝 I’ll use my recipe to create unit tests.

Example #1: testing a GET to an API

Let’s say your app depends on an external API to fetch the user’s profile — a query. We need to test that ProfileGateway handles it well, namely the parsing and proper transformation of data.

General recipe

  1. arrange: prepare Javalin with a single handler only to simulate your external API endpoint; inside the handler, write a stubbed response as if you were the API owner;
  2. act: call the subject method that fetches the data;
  3. assert: test that your subject correctly parsed the stubbed response (API JSON → your domain representation); optionally, you can check the number of calls and HTTP details (e.g. if you sent the proper headers).

Example #2: testing a POST to an API

Now we’ll see an example of a command — there’s a side-effect to be tested. In this case, we need to assert that the data was properly prepared and posted to the third party by the ProfileGateway. The HTTP call details can be tested as well.

General recipe

  1. Arrange: prepare Javalin with a single handler only to simulate your external API endpoint; inside the handler, store what you want to assert later, like path, headers, and body;
  2. Act: call the subject method that executes the side effect;
  3. Assert: test that the stored values in the handler are correct; for example, the body must have been properly converted to the external API (your domain representation → API JSON).

⚠️ Whatever you do, never do assertions inside the Javalin test handler. Why? Because if they fail, they’ll throw a JUnit exception, which is swallowed by Javalin; the test will be green! Always do the assertions in the end hence following the Arrange, Act, Assert pattern.

Alternative approaches

It’s important to mention alternatives to the Javalin proposal:

  • 🛑 Mocking the HTTP client (e.g. with MockK, HttpClientMock) when httpClient.get is called with ... respond with ... You’d be mocking what you don’t own; would you mock a database driver? Mocking a REST client would be as bad. You’d couple the test with the HTTP client, which is an implementation detail. With Javalin, it doesn’t matter which REST client you use in your implementation (Java HTTP client, Apache HTTP Client, Retrofit, etc).
  • Mocking a wrapper around the HTTP client You’re creating just an additional pass-through layer. Also, you’re not emulating HTTP anyway. Finally, you’re not abstracting the external party data model. Using Javalin, you have the (localhost) network involved so it’s much more realistic. For example, it’s trivial to simulate network errors (e.g. 401 Unauthorized) to see how your system handles them.
  • Using a library that mocks the language HTTP layer (e.g. vrc, ExVCR, VCR.py) If it’s a well-supported library and it’s not coupled to any HTTP client in concrete, I see this as a good alternative. It’s not as realistic as my proposal but it’s worth considering.
  • Using a simulator for HTTP-based APIs (e.g. MockServer, WireMock, JSON Server) The same technique as the Javalin proposal but with a dedicated library with a built-in DSL. I tried both and ended up replacing them with Javalin because I didn’t want to learn their DSL. In addition, when a test fails, Javalin makes it easier to fix it. (The equivalent for Python is the HTTPretty’s library but there it doesn’t look so odd.)
  • Integration, system, contract, and end-to-end testing These are complementary to unit testing rather than replacing it.

📝 If you’re using Javalin as your app web server, it’s even better, as you won’t add extra libraries for testing.

You can find the given examples in a GitHub repo (test and implementation).

Kotlin
Javalin
Unit Testing
Mockserver
Wiremock
Recommended from ReadMedium