这是我参与「第三届青训营 -后端场」笔记创作活动的第5篇笔记
如果没有测试,自然我们也无法保证代码的正确性。而与一般的单元测试相比,这里要稍微复杂一些。 因为这里有比较复杂的关系,需要构造HTTP请求,并且还要生成响应。 幸运的是,Go语言本身提供了一个名为httptest的包,它可以让我们更方便地测试HTTP请求。
姑且假设我们用了gin框架(实际上httptest是为标准库net/http提供的),我们稍微加强一下场景。 假设我们有一个这样的 gin.HandlerFunc 函数:
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
func MyHandler(c *gin.Context) {
if c.ShouldBindJSON(&person) != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "success"})
}
假设它将被注册到一个gin.Engine上,处理 /person 路径的请求。
那么我们可以用如下的代码来测试这个函数
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/person"))
c.Request.PostForm = &url.Values{
"name": []string{"foo"},
"age": []string{"18"},
}
r.ServeHTTP(w, c.Request)
具体来说我们用httptest构造了一个mock的响应输出(用于handler写入数据),基于此借助gin构造了测试的用的请求对象。 然后我们对req这个请求对象进行一些值的填充。也就是说测试中request对象并不是让我们手动写一个类似 请求行的表示。
注意,这里的request就是我们在默认的http.HandlerFunc中使用的request对象。
因此如果有一些别的数据需要填充,可以直接使用request对象的方法。 这有点类似我们写http中间件的场景。 这里比较简单,我们只是假设了一个name和age的字段,并且填充了它们。 最后一行的 r 是一个gin的Engine。
调用的所谓的ServeHTTP方法其实就相当于在这两个参数之上调用了一个http.HandlerFunc处理它们。
经过了这个处理之后,我们可以检测到w的状态,用以确认handler的执行是否正确。
比如说这里可以按如下方式检测:
if w.Code != http.StatusOK {
t.Errorf("Status code is %d, expected %d", w.Code, http.StatusOK)
}
if !strings.Contains(w.Body.String(), `{"message":"success"}`) {
t.Errorf("Body is %q, expected %q", w.Body.String(), `{"message":"success"}`)
}