Fork me on GitHub

go test之httptest

在使用go语言进行编程的时候,go test命令方便的让我们进行函数单元测试。

1
$go test -v

但是当被测试函数依赖于环境(读取硬件路由表)或者server端实现(发送http请求给server端),本地运行go test执行单元测试就会出现问题。这就需要我们在本地测试代码中将待测试函数中依赖外部的实现部分进行打桩(返回构造的假结果)。

本文以http请求为例,使用httptest包代替server端,构造http返回结果。

目录结构:

peron.go文件,待测函数GetInfo( )

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
package person

import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)

type Person struct {
Name string `json:"name"`
Address string `json:"address"`
Age int `json:"age"`
}

func GetInfo(api string, addr string) ([]Person, error) {
url := fmt.Sprintf("%s/person?addr=%s", api, addr)
resp, err := http.Get(url)
if err != nil {
return []Person{}, err
}

if resp.StatusCode != http.StatusOK {
return []Person{}, fmt.Errorf("get info didn’t respond 200 OK: %s", resp.Status)
}

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("read response failed")
}

p := make([]Person,0)
err = json.Unmarshal(body,&p)
if err != nil {
return nil, fmt.Errorf("unmarshal response failed")
}
return p, nil
}

peron_test.go文件:

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
package person

import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
)

var personResponse = []Person{
{
Name : "wahaha",
Address : "shanghai",
Age : 20,
},
{
Name : "lebaishi",
Address : "shanghai",
Age : 10,
},
}

var personResponseBytes, _ = json.Marshal(personResponse)

func TestPublishWrongResponseStatus(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write(personResponseBytes)
if r.Method != "GET" {
t.Errorf("Expected 'GET' request, got '%s'", r.Method)
}
if r.URL.EscapedPath() != "/person" {
t.Errorf("Expected request to '/person', got '%s'", r.URL.EscapedPath())
}
r.ParseForm()
topic := r.Form.Get("addr")
if topic != "shanghai" {
t.Errorf("Expected request to have 'addr=shanghai', got: '%s'", topic)
}
}))
defer ts.Close()

api := ts.URL
fmt.Println("url:", api)
resp, err := GetInfo(api, "shanghai")
if err != nil {
fmt.Println("ERROR:", err.Error())
t.Error(err)
} else {
fmt.Println("Rsp:", resp)
}
}


func TestPublishWrongResponseStatusWithFailed(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusBadRequest)
w.Write(personResponseBytes)
if r.Method != "GET" {
t.Errorf("Expected 'GET' request, got '%s'", r.Method)
}
if r.URL.EscapedPath() != "/person" {
t.Errorf("Expected request to '/person', got '%s'", r.URL.EscapedPath())
}
r.ParseForm()
topic := r.Form.Get("addr")
if topic != "shanghai" {
t.Errorf("Expected request to have 'addr=shanghai', got: '%s'", topic)
}
}))
defer ts.Close()

api := ts.URL
fmt.Println("url:", api)
resp, err := GetInfo(api, "not_shanghai")
if err != nil {
fmt.Println("ERROR:", err.Error())
} else {
fmt.Println("Rsp:", resp)
}
}

在person目录下执行测试命令:

1
$go test -v

输出:

1
2
3
4
5
6
7
8
9
10
11
=== RUN   TestPublishWrongResponseStatus
url: http://127.0.0.1:44973
Rsp: [{wahaha shanghai 20} {lebaishi shanghai 10}]
--- PASS: TestPublishWrongResponseStatus (0.00s)
=== RUN TestPublishWrongResponseStatusWithFailed
url: http://127.0.0.1:45683
--- FAIL: TestPublishWrongResponseStatusWithFailed (0.00s)
person_test.go:69: Expected request to have 'addr=shanghai', got: 'not_shanghai'
FAIL
exit status 1
FAIL person 0.009s
您的鼓励是我持之以恒的动力