I recently shared an overview article about Stubbing External Services in Rails. I found it when looking for the best way to stub a pletora of services in a microservices environment. Sure, docker (or whatever) everything and run it locally / in your test suite. But unless you’ve plenty of disk- and memory space, this isn’t always a viable option. The alternative: simulate the service. Mock or stub the endpoint.
The go to gems are Webmock, which catches request and allows you to define the responses explicitly and VCR, which allows you to record responses, and play back.
VCR is quite cool, but as it is a recorder of earlier responses, there may be a lot of noise to dig trough when trying to make the responses a bit more generic (dealing with random token requests and what else)
For testing I personally prefer Webmock, as it records the request, and offers you enough flexibility to tailer the essential request parameters, headers and of course the response. Kind of like VCR, but using the suggested code is fully optional.
Another alternative to mocking in code is dependency injection (I had actually never considered this option for testing), which replaces the entire local interface that deals with external connections. For short request cycles I’ve often also often adopted simple stubbing of the methods that should return some external value.
Docker would allow us to do this with the ‘real’ service, but as said, to keep things light weight in your test suite running a Docker to test against is quite a burden on your system resources. Hence, this approach is about making a custom service purely for testing. The nice thing about having a local service is also that while automated testing is a lofty goal I often prefer to also see and click through how things operate from the user’s perspective while in development.
But before you start writing a fake local service, you might run into rebuilding almost the functionality of the service you’re replacing. Another thing to be cautious about: you may create a too simplistic server response, only supporting the happy flow. To counter balance these concerns I recently took the test driven approach: recycling the code I used to make my Webmocks work. Since my test should be response-variants-complete (I know, ideally), my test server should also be response-variants complete. And if I could break the system while testing the front end myself I also know that my automated test of the service interface isn’t complete. Hence it could work both ways.
Webmock works with a simple structure: a hash with request parameters, a hash with request headers, and a response hash containing both headers and the body. It is not hard to extract these parameters and store in a dedicated structure. Be it a database of a simple global constant.
This allows for reuse outside webmock. But it requires you to think about how to create predictable responses. In our setting this turned out not to be too hard (think e.g. of using a product name as a token for the result that is expected). As an avid user of plain old fixtures I don’t see too many problems here, but as a heavy FactoryBot user you might be sceptical about this approach.
Having a global configuration of your Webmocks comes with the advantage of allowing the configuration to be reused. For example in a controller that exposes an API fully compatible with the API of the real service. You know this API is fully compatible because you use the same configuration in your tests. So instead of pointing your local development environment to a remote test-service or local Docker, point it to point to your mock-running-on-localhost-service. With the introduction of multi process/threaded servers (thinking of Puma for Rails) you can, even in development, create an endpoint in the same app that you are developing. Your service simulates your API using the same configuration as you used in the mocks you used for testing. You can use it offline and while on the road. And it keeps your laptop silent. And it works instantly for any other colleague.
For our project mocking the services worked quite well, but love to hear your thoughts on our approach.
Enjoyed this? Follow me on Mastodon or add the RSS, euh ATOM feed to your feed reader.
Dit artikel van murblog van Maarten Brouwers (murb) is in licentie gegeven volgens een Creative Commons Naamsvermelding 3.0 Nederland licentie .