Integrate with httpmock
Overview
httpmock is often used for HTTP-related unit tests: when you write a function that involves remote HTTP API calls, and want to make it covered by unit tests, but if you make remote HTTP calls in unit tests, it will fail if the network is unreachable or the remote server API is unavailable. Therefore, we generally use httpmock to intercept our requests and directly return the data we expect to return, this makes it possible to complete HTTP-related unit tests directly without going through the network transportWrapper.
This article will show you how to write unit tests using httpmock if your project uses req
as the http client.
Principle Introduction
httpmock mainly implements request interception by replacing the Transport
of http.Client
, so that the originating Request
does not go through the network but directly returns the corresponding Response
according to the configuration of httpmock. client.GetClient()
can get the internal http.Client
, which we pass to httpmock to replace the default Transport
of req
to integrate with httpmock.
The dump capability and part of the debug log capability of req come from its own implementation of Transport, which will be lost after httpmock replaces Transport.
Simple Example
Suppose we have written a function to get the user name of the specified GitHub account:
import (
"fmt"
"github.com/imroc/req/v3"
)
func GetUserName(client *req.Client, loginName string) (name string, err error) {
var user struct {
Name string `json:"name"`
}
resp, err := client.R().
SetSuccessResult(&user).
SetPathParam("loginName", loginName).
Get("https://api.github.com/users/{loginName}")
if err != nil {
return
}
if !resp.IsSuccessState() {
err = fmt.Errorf("bad status code %q, body:%s", resp.StatusCode, resp.String())
return
}
name = user.Name
return
}
Let’s write a unit test using httpmock:
import (
"github.com/imroc/req/v3"
"github.com/jarcoal/httpmock"
"net/http"
"testing"
)
func TestGetUserName(t *testing.T) {
client := req.C()
httpmock.ActivateNonDefault(client.GetClient())
httpmock.RegisterResponder("GET", "https://api.github.com/users/imroc", func(request *http.Request) (*http.Response, error) {
respBody := `{"login":"imroc","id":7448852,"node_id":"MDQ6VXNlcjc0NDg4NTI=","avatar_url":"https://avatars.githubusercontent.com/u/7448852?v=4","gravatar_id":"","url":"https://api.github.com/users/imroc","html_url":"https://github.com/imroc","followers_url":"https://api.github.com/users/imroc/followers","following_url":"https://api.github.com/users/imroc/following{/other_user}","gists_url":"https://api.github.com/users/imroc/gists{/gist_id}","starred_url":"https://api.github.com/users/imroc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/imroc/subscriptions","organizations_url":"https://api.github.com/users/imroc/orgs","repos_url":"https://api.github.com/users/imroc/repos","events_url":"https://api.github.com/users/imroc/events{/privacy}","received_events_url":"https://api.github.com/users/imroc/received_events","type":"User","site_admin":false,"name":"roc","company":"Tencent","blog":"https://imroc.cc","location":"China","email":null,"hireable":true,"bio":"I'm roc","twitter_username":"imrocchan","public_repos":137,"public_gists":0,"followers":407,"following":155,"created_at":"2014-04-30T10:50:46Z","updated_at":"2022-05-03T12:12:52Z"}`
resp := httpmock.NewStringResponse(http.StatusOK, respBody)
resp.Header.Set("Content-Type", "application/json; charset=utf-8")
return resp, nil
})
name, err := GetUserName(client, "imroc")
if err != nil {
t.Error(err)
}
expectedName := "roc"
if name != expectedName {
t.Errorf("bad name result, expected %q, got %q", expectedName, name)
}
}
Write unit tests for the SDK
Taking the GitHub SDK in Build SDK Quickly with Req as an example, let’s write a unit test for its GetUserProfile
method:
import (
"github.com/imroc/req/v3"
"github.com/jarcoal/httpmock"
"net/http"
"testing"
)
func TestClient_GetUserProfile(t *testing.T) {
github := NewClient()
httpmock.ActivateNonDefault(github.GetClient())
httpmock.RegisterResponder("GET", "https://api.github.com/users/imroc", func(request *http.Request) (*http.Response, error) {
respBody := `{"login":"imroc","id":7448852,"node_id":"MDQ6VXNlcjc0NDg4NTI=","avatar_url":"https://avatars.githubusercontent.com/u/7448852?v=4","gravatar_id":"","url":"https://api.github.com/users/imroc","html_url":"https://github.com/imroc","followers_url":"https://api.github.com/users/imroc/followers","following_url":"https://api.github.com/users/imroc/following{/other_user}","gists_url":"https://api.github.com/users/imroc/gists{/gist_id}","starred_url":"https://api.github.com/users/imroc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/imroc/subscriptions","organizations_url":"https://api.github.com/users/imroc/orgs","repos_url":"https://api.github.com/users/imroc/repos","events_url":"https://api.github.com/users/imroc/events{/privacy}","received_events_url":"https://api.github.com/users/imroc/received_events","type":"User","site_admin":false,"name":"roc","company":"Tencent","blog":"https://imroc.cc","location":"China","email":null,"hireable":true,"bio":"I'm roc","twitter_username":"imrocchan","public_repos":137,"public_gists":0,"followers":407,"following":155,"created_at":"2014-04-30T10:50:46Z","updated_at":"2022-05-03T12:12:52Z"}`
resp := httpmock.NewStringResponse(http.StatusOK, respBody)
resp.Header.Set("Content-Type", "application/json; charset=utf-8")
return resp, nil
})
user, err := github.GetUserProfile("imroc")
if err != nil {
t.Error(err)
}
expectedName := "roc"
if user.Name != expectedName {
t.Errorf("bad name result, expected %q, got %q", expectedName, user.Name)
}
}