We often build server applications that talk to other backend services, mostly over HTTP. Such an application essentially receives requests, collects data from other services and transforms them into a response.
How to test communication with external, 3rd party services?
How do you test such applications?
The tests should run offline and deterministic. Hence, calling the external services when running the tests is no option. This would make the tests non-deterministic and slow. We might even run into rate-limits and fraud-detection related issues. So, we have to emulate the external services in some manner.
Here are the most usual option, I am aware of. You can use several in one project of course.
Option 1: Start the external service locally
Sometimes you can start the service in a Docker container or similar. For example, you can start Mailpit to test outgoing emails, or Keycloak for OpenID authentication.
I prefer this approach when the external service is publicly available, quick to start, easy to configure and the API rather stable.
Option 2: Hide the communication behind strategies
You can use the Strategy pattern to replace the communication with external services by local, predefined responses. Most of your own code is still executed during tests, the part sending the requests and receiving the responses is not. Also, your code becomes more complex, since you have to add the strategy pattern and provide mock implementations for testing.
I prefer this approach if the code already contains a strategy pattern, like storage in database, cloud or on disk. Then I like to tests the storage strategies individually and add an in-memory storage for other tests.
Option 3: Implement service mocks
You can implement a simplified version of each service which listens on a socket. They behave close enough to the real services (hopefully) for your tests. You would have to validate incoming requests and generate realistic responses.
If you have few services with small and simple interfaces I like this approach. Also, it allows to inject errors (500 internal server errors) and such to test error handling and recovery.
Option 4: Request/Response recording and replay
This option is similar to option 3, but with more automation. With libraries like go-vcr you can automatically record and replay calls to external services. You have to replace the HTTP client during tests by a proxy.
While writing tests, the HTTP client proxy behaves like a production one: send the request and receive the response. Both are written to disk. While running tests, the HTTP client proxy uses the information on disk to find a response for each request. No network communication happens.
Additionally, you can review the external communication in the recording file.
One remark though: beware of secrets.
More often than not, the 3rd party service requires an API key, access tokens or similar.
Do not forget to remove them from the recording file before committing it to the repository.
Here is a small example how to do that in go-vcr:
I prefer this approach, if the application under test communicates a lot with various external system.
As always, thanks for reading and feel free to share your opinion with us.