Mocking Spring RestTemplate

Vineeth Venudasan
CodeX
Published in
3 min readNov 19, 2021

Just don’t do it.

I’ve seen the following line of code (or it’s corresponding version in other languages) way too many times to be comfortable with it.

val mockRestTemplate = mockk<RestTemplate>()

I think it’s a terrible idea.

Photo by Volodymyr Hryshchenko on Unsplash

Don’t mock what you don’t own.

Why are tests that mock framework elements considered bad?

They don’t help with refactoring

Tests that mock out library components usually cannot be used for refactoring. The main reason for this is because these kind of tests usually “follow the code”. Due to this, we end up having to change test expectations every time you refactor code, even when the logic is unchanged.

As such, a major benefit of doing TDD is thrown right out of the window.

Let me present an example.

Say you have an APIClient class that makes a GET call to an external endpoint using a RestTemplate instance contained within in. When testing the APIClient class, the RestTemplate is mocked out and expectations are set on it.

every { 
template.postForEntity(
"/customers/4520",
any(),
CustomerDetailsResponse::class.java
)
} throws HttpServerErrorException("5xx from server")

The mock is set up so that every POST call to the /customers/4520 endpoint will simulate the server responding with a 500 Internal Server Error.

The APIClient code that handles this 5xx response is then tested, using the mocked RestTemplate instance. All is good for today, the code is checked in, the pipelines are green, time for a pint at the local bar.

However — what happens if for some reason, we change the implementation, but the logic remains the same?

Say we use RestTemplate#exchange instead of RestTemplate#postForEntity ?

Now we see that we have to update the test expectations, even though there was no change in logic. The tests are no longer a safety net for the change that is about to be made.

Limits a future upgrade

Also — we cannot swap out the library for another one that does the exact same thing, without re-writing the tests. Again, this means there is no safety net for this change, which does not make it any better than not having written tests in the first place.

Assumptions about external code

https://xkcd.com/1339/

In the example earlier we assume that the RestTemplate throwing a specific exception implies the remote server responded with a HTTP 500 Internal Server Exception.

This is an assumption about the semantics and the contracts between your application and the library.

If a future upgrade of the library were to change these semantics, there is no safety net anymore, and a bug slips through undetected.

If this assumption was made in many places in the code-base, that the impact is much more.

What’s the alternative?

Mock-servers provided by the library

Most libraries provide a fake implementation of a web-server so that you can test your API client classes.

However, I end up being a bit vary about them, because they are in the end, ‘tied’ to the library. In the event of swapping out the library for another, these kind of tests may not help.

WireMock

This is my preferred approach these days, chosen mostly for the maturity of the tool. Since they are completely independent from implementation detail, I believe they can be more resilient in the time to come.

There are guides you will be be able to find that will detail how to use WireMock in the JVM, and I suggest you have a look if it suits your needs. I’ve added the official Getting Started with JUnit5 reference at the bottom.

Conclusion

Don’t forget value when it comes to tests.

If tests only help us with double entry book-keeping when it comes to writing code, but does not provide a safety net for future change, I believe value is on the lower end of what you can have.

As such, choose to have a more resilient approach to testing API clients.

References

  1. https://8thlight.com/blog/eric-smith/2011/10/27/thats-not-yours.html
  2. Dont Mock Types you don’t own https://testing.googleblog.com/2020/07/testing-on-toilet-dont-mock-types-you.html
  3. Using Wiremock with JUnit 5 http://wiremock.org/docs/junit-jupiter/

--

--

Vineeth Venudasan
CodeX
Writer for

Backend Engineer / Software Consultant. The views expressed here are my own and does not necessarily represent my employer’s positions, strategies or opinions