How to stub requests to remote hosts with Go

When creating a package with a client for some API, it’s critical to cover it with tests, especially if you want to put this package on GitHub for public usage, since the bug that got into the new version can bring to deplorable consequences for projects that use it in production environments. After making the package public it’s your job to track its reliability and compatibility to make it usable by others.

Testing helps code maintainability, so when other users create pull requests with changes they make, you will always see if something broken, and be sure that the code is still reliable, especially when you’re using some CI tool with GitHub integration like Travis, Semaphore or Jenkins.

Recently I was looking through various packages that implement clients for APIs and had run into the lack of tests in them, although such code is easy to test by stubbing requests to the remote host. So I decided to share how I’m testing my clients.

The code above defines client what gives the ability to receive a list of users from the remote server, the request occurs through http.Client and by redefining itsTransport property with custom http.Transport we can redirect all requests that the client makes to another fake server, which we can create using the httptest.NewServer function and return the responses we need.

The function above receives http.Handler interface as an argument in which we can write response body, set status code, or anything else we are expecting from the remote host, we even can check request what we are sending. On the end, the function returns http.Client with redefined Transport, and teardown function, which will stop the fake server, launched previously. Thus, we have the ability to write some test with the stubbed request.

Since our library will be used in other packages, it will be also good to give the ability to change the httpClient property by other packages, as it may be used in integration tests, for example, if our client will be injected as a dependency. You may do it by simply making httpClient *http.Client property public by renaming it to HTTPClient *http.Client, or by adding the func(cli *Client) SetHTTPClient(httpClient *http.Client) method for the client, but personally I prefer and think the best way is to use functional options as it’s very powerful tool.

The full example can be found on GitHub.