If you ever find yourself needing intercept a http client in go, httptest provides a facility to do just that. httptest.NewServer
lets you stand up a temporary server that you can control in your test case.
We will use this function as an example of writing unit tests that use httptest.NewServer.
func topPostOnSubreddit(r string) (string, error) {
resp, err := http.Get(fmt.Sprintf("https://reddit.com/r/%s", r))
if err != nil {
return "", err
}
defer resp.Body.Close()
dec := json.NewDecoder(resp.Body)
var data redditData
err = dec.Decode(&data)
if err != nil {
return "", err
}
top := findTopPost(data)
return top["title"].(string), nil
}
Refactor
Let refactor this so we can use httptest.NewServer in our unit tests. The required changes are ...
An instance of http client has to be passed in
func topPostOnSubreddit(client *http.Client, r string) (string, error) {
req, err := http.NewRequest("GET", fmt.Sprintf("https://reddit.com/r/%s", r), nil)
if err != nil {
return "", err
}
resp, err := client.Do(req)
// ...
}
The base of the url has to be configurable by the caller
func topPostOnSubreddit(client *http.Client, fullUrl string) (string, error) {
req, err := http.NewRequest("GET", fullUrl, nil)
if err != nil {
return "", err
}
resp, err := client.Do(req)
// ...
}
Writing the Test Case
The returned test server has both the client and url available for you to pass to the function. When the function uses the client makes the http call the handlerFunc you defined in your test case is called. This allows you to inject fixture data into the runtime to control unit test behavior.
func TestTopPostOnSubreddit(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
enc := json.NewEncoder(w)
// this is where your test fixture goes
testData := redditData{}
err := enc.Encode(&testData)
require.NoError(t, err)
}))
t.Cleanup(server.Close)
url := fmt.Sprintf("%s/r/android.json", server.URL)
top, err := topPostOnSubreddit(server.Client(), url)
require.NoError(t, err)
require.Equal(t, "foobar", top)
}
Top comments (0)