Go Unittest for Http Client/Server

The httptest package provides utilities for HTTP testing:

  • Test server handler logic by mocking the request.
  • Test client logic by mocking the server handler.

For server handler testing, you need ResponseRecorder object to record the http.ResponseWriter’s mutations, plus httptest.NewRequest and pass them into the server handler for (w http.ResponseWriter, r *http.Request).

For client logic testing, you need to mock specific server handler or even entire server, using the httptest.NewServer Object or other server types.

From the main page, it provides types for:

  • ResponseRecorder
  • NewTLSServer
  • Server
  • Server(HTTP2)

For client logic testing, if you only set up one handler and call it repeatedly, it returns the same response. If you do want to have different response when you call mock server, here is the example, I enrich it by adding more in handlers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package main

import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"net/url"
"strings"
)

func main() {
responseCounter := 0
responses := []func(w http.ResponseWriter, r *http.Request){
// Transfer into upper case for word provided
// Hand GET request
func(w http.ResponseWriter, r *http.Request) {
query, err := url.ParseQuery(r.URL.RawQuery)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "invalid request")
return
}
word := query.Get("word")
if len(word) == 0 {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "missing word")
return
}
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, strings.ToUpper(word))
},
// Handle POST request to get message
func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodPost {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Println(err)
return
}
// Parse the request body as JSON.
var data struct {
Name string `json:"name"`
Email string `json:"email"`
}
err = json.Unmarshal(body, &data)
if err != nil {
fmt.Println(err)
return
}
// Print the name and email of the user who submitted the form.
fmt.Println("Name:", data.Name)
fmt.Println("Email:", data.Email)
fmt.Println("Header:", r.Header)
}
},
}
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
responses[responseCounter](w, r)
responseCounter++
// Using counter to rotate if you have many more calls
responseCounter = responseCounter % 2
}))
defer ts.Close()

// call from client with different http method
clientGetMethod(ts.URL + "?word=school")
// bad call, will get your error message
clientGetMethod(ts.URL + "?apple")
clientPostMethod(ts.URL)
}

func clientGetMethod(url string) {
res, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
resBody, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n\n", resBody)
}

func clientPostMethod(url string) {
// Set the request body.
var jsonStr = []byte(`{
"name": "John Doe",
"email": "johndoe@example.com"
}`)
req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(jsonStr))
if err != nil {
fmt.Println(err)
return
}
// Set the request header.
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Println(err)
return
}
// Close the response body.
defer resp.Body.Close()
}
0%