How to test HTTP related code in Kotlin via mock-server
Mock is the answer to this kind of case when you need to deal with the IO thing like network request. Such that, you can test your logic without worrying about the underneath implementation. But sometimes it’s not the case. Integration test is the answer.
1. Use case
My case is I have an expect
class where I need to implement in across several platforms. Let’s say there is a httpGet()
method. In terms of Android, I use the HttpURLConnection
to implement the httpGet()
method. If I want to test whether it works or not, I just need to mock HttpURLConnection
and assert whether it has been set up with correct parameters before sending. But it’s not ideal. Because in the end, I might change to another lib, I might just wrap around some famous 3rd party lib to do the job. But at that time, I need to refactor the tests as well. Because the current tests are locked to testing the detail which is coupled with HttpURLConnection
.
So instead, I want to write some integration tests which will send actual requests. Then assert the response to see whether everything works or not.
2. Benefits
- No mock at all. The tests are as clean as insanely easy unit tests. You call a method and assert its return value.
- You can move the tests to the
common
code, such that, you just need to write the tests once, and it will test for all platforms.- If you don’t know what does
common
code mean here, it’s code which meant to be shared across without change a single line. It’s from the Kotlin multi-platform project. - You can also check my blog for how to set up a codebase where you can share code among Android, iOS, JVM, JS.
- It’s not as easy as it sounds because you may use different
MockServer
across platforms and they may have different API. But I think it should work after adding another layer to abstract the differences.
- If you don’t know what does
3. Setup
We will use this one: MockServer. And first thing first, add a dependency to your build.gradle
.
1 | testCompile 'org.mock-server:mockserver-netty:5.3.0' |
You can always find the latest version number on their Github release page.
4. Add an abstract class for setting up the server
1 | import org.mockserver.client.server.MockServerClient |
OK, it’s as simple as it will start a new server on a random port. Open it before each test and close it after each test.
The reason we need that random port number is we might need the server for multiple test suites and we don’t want they have conflicts with each other.
The official documentation mentions you can setup a sessionID or correlationId header to run tests in parallel. I don’t use it because I don’t want to modify my request format just for the tests.
5. Write the test
Now you can write the integration tests in an easy way.
1 | class PostTest: HttpTestBase() { |
It should be pretty straightforward.
6. Some extension methods
In fact, mockServer.setup()
and mockServer.verifyRequest()
are not from MockServer
. I wrapped them for easy access.
1 | internal fun MockServerClient.setup( |
From here, you can know the standard way to set up the MockServer and verify its request.
7. End
Actually, you can further simplify this, if you just want to have a single server if you just to have one server setup for all tests such that you don’t need to invoke mockServer.setup()
in each test. You just need to set it up in a @BeforeTest
or @BeforeAll
.
Thanks for reading!
Follow me (albertgao) on twitter, if you want to hear more about my interesting ideas.