Golang单元测试之httptest使用

前端之家收集整理的这篇文章主要介绍了Golang单元测试之httptest使用前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

现在有一个需求那就是,我们需要使用Golang的net/http包中的http.Get(url)方法去向服务器端请求数据,但是负责服务端的同事并没有将接口实现(可能是同事太忙,把妹,喝酒,扯淡, XO等等)以至于你只知道返回数据的json格式,然而无法请求到真实的数据,但是你的工作进度并不能因为同事而耽误,需要测试你的代码的正确性,那么怎么办?办法就是通过单元测试中的httptest,实现http server,并设置好返回值,那么http.Get(url)的请求就会直接打到单元测试的http server上,同时得到你设置好的返回值,你就可以继续去处理数据,测试你的代码逻辑了。


一、导入

设置一个场景:

需要向服务器端去获取住址为"shanghai"的所有人的信息,请求函数为:

  1. func GetInfo(api string) ([]Person,error)
这个方法包括

1.向服务端发送get请求获取数据

2.将数据序列化person的信息

3.如果出现错误,返回error

需要测试的内容

1.需要测试返回值是我们设置的值:

  1. var personResponse = []Person{
  2. {
  3. Name : "wahaha",Address : "shanghai",Age : 20,},{
  4. Name : "lebaishi",Age : 10,}
2.需要测试GetInfo方法是发送了正确的http请求,及method是否正确,路径是否正确,请求参数是否正确。

3.返回状态码是否正确。


二、Golang testing基础

Go提供了一个testing包来写单元测试。假设我们有个文件叫person.go,那么我们的测试文件就需要明明为person_test.go

  1. package person
  2.  
  3. import (
  4. "testing"
  5. )
  6.  
  7. func TestPublishUnreachable(t *testing.T) {
  8. api := "http://localhost:8090"
  9. _,err := GetInfo(api)
  10. if err != nil {
  11. t.Errorf("GetInfo() return an error")
  12. }
  13. }
注:

>测试函数以Test*开头。

>测试函数将*testing.T作为参数,可以在失败的情况下使用Errorf()方法

>在包内使用go test来运行单元测试。


这个单元测试将会失败,因为我们还没有实现GetInfo()方法,下面是person.go文件

  1. package person
  2.  
  3. import (
  4. "net/http"
  5. "fmt"
  6. )
  7.  
  8. const (
  9. ADDRESS = "shanghai"
  10. )
  11.  
  12. type Person struct {
  13. Name string `json:"name"`
  14. Address string `json:"address"`
  15. Age int `json:"age"`
  16. }
  17.  
  18. func GetInfo(api string) ([]Person,error) {
  19. url := fmt.Sprintf("%s/person?addr=%s",api,ADDRESS)
  20. resp,err := http.Get(url)
  21.  
  22. if err != nil {
  23. return []Person{},err
  24. }
  25.  
  26. if resp.StatusCode != http.StatusOK {
  27. return []Person{},fmt.Errorf("get info didn’t respond 200 OK: %s",resp.Status)
  28. }
  29.  
  30. return nil,nil
  31. }
当然运行go test也会返回一个错误,因为请求地址的问题,请求的并不是一个实际上的http server,那么自然也不会有正常的返回。

三、Golang httptest

上面一个例子很有用,但是如何去发送一个真正的http request而不去真正的启动一个http server(亦或者请求任意的server)?答案是使用Go 的httptest包,这个包可以非常简单的创建一个测试的http server,那么下面我们将展示一下完整的代码,并解释一下整体的测试流程:

person.go:

  1. package person
  2.  
  3. import (
  4. "net/http"
  5. "fmt"
  6. "io/IoUtil"
  7. "encoding/json"
  8.  
  9. "github.com/astaxie/beego/logs"
  10. )
  11.  
  12. const (
  13. ADDRESS = "shanghai"
  14. )
  15.  
  16. type Person struct {
  17. Name string `json:"name"`
  18. Address string `json:"address"`
  19. Age int `json:"age"`
  20. }
  21.  
  22. func GetInfo(api string) ([]Person,err := http.Get(url)
  23.  
  24. defer resp.Body.Close()
  25.  
  26. if err != nil {
  27. return []Person{},resp.Status)
  28. }
  29.  
  30. bodyBytes,_ := IoUtil.ReadAll(resp.Body)
  31. personList := make([]Person,0)
  32. err = json.Unmarshal(bodyBytes,&personList)
  33. if err != nil {
  34. logs.Error("decode data fail")
  35. return []Person{},fmt.Errorf("decode data fail")
  36. }
  37.  
  38. return personList,nil
  39. }

person_test.go:

  1. package person
  2.  
  3. import (
  4. "testing"
  5. "net/http/httptest"
  6. "net/http"
  7. "fmt"
  8. "encoding/json"
  9. )
  10.  
  11. var personResponse = []Person{
  12. {
  13. Name : "wahaha",}
  14.  
  15. var personResponseBytes,_ = json.Marshal(personResponse)
  16.  
  17. func TestPublishWrongResponseStatus(t *testing.T) {
  18. ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter,r *http.Request) {
  19. w.WriteHeader(http.StatusOK)
  20. w.Write(personResponseBytes)
  21. if r.Method != "GET"{
  22. t.Errorf("Expected 'GET' request,got '%s'",r.Method)
  23. }
  24. if r.URL.EscapedPath() != "/person" {
  25. t.Errorf("Expected request to '/person',r.URL.EscapedPath())
  26. }
  27. r.ParseForm()
  28. topic := r.Form.Get("addr")
  29. if topic != "shanghai" {
  30. t.Errorf("Expected request to have 'addr=shanghai',got: '%s'",topic)
  31. }
  32. }))
  33.  
  34. defer ts.Close()
  35. api := ts.URL
  36. fmt.Println("url:",api)
  37. resp,_ := GetInfo(api)
  38.  
  39. fmt.Println("reps:",resp)
  40. }
解释一下:

>我们通过httptest.NewServer创建了一个测试的http server

>读请求设置通过变量r *http.Request,写变量(也就是返回值)通过w http.ResponseWriter

>通过ts.URL来获取请求的URL(一般都是<http://ip:port>)

>通过r.Method来获取请求的方法,来测试判断我们的请求方法是否正确

>获取请求路径:r.URL.EscapedPath(),本例中的请求路径就是"/person"

>获取请求参数:r.ParseForm,r.Form.Get("addr")

>设置返回的状态码:w.WriteHeader(http.StatusOK)

>设置返回的内容(这就是我们想要的结果):w.Write(personResponseBytes),注意w.Write()接收的参数是[]byte,因此需要将object对象列表通过json.Marshal(personResponse)转换成字节。


综上,我们可以通过不发送httptest来模拟出httpserver和返回值来进行自己代码的测试了。


Author:忆之独秀

Email:leaguenew@qq.com

注明出处:http://www.jb51.cc/article/p-pioxewyr-bpo.html

猜你在找的Go相关文章