Golang单元测试之httptest使用

转载:https://blog.csdn.net/lavorange/article/details/73369153 

—————————————————————————————————————————————————

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

一、导入

设置一个场景:

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

func GetInfo(api string) ([]Person, error) 


这个方法包括:


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

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

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

需要测试的内容:

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

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

2.需要测试GetInfo方法是发送了正确的http请求,及method是否正确,路径是否正确,请求参数是否正确。
3.返回状态码是否正确。

二、Golang testing基础

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

package person
 
import (
    "testing"
)
 
func TestPublishUnreachable(t *testing.T) {
    api := "http://localhost:8090"
    _, err := GetInfo(api)
    if err != nil {
        t.Errorf("GetInfo() return an error")
    }
}


注:
>测试函数以Test*开头。

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

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

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

package person
 
import (
    "net/http"
    "fmt"
)
 
const (
    ADDRESS = "shanghai"
)
 
type Person struct {
    Name string `json:"name"`
    Address string `json:"address"`
    Age int `json:"age"`
}
 
func GetInfo(api string) ([]Person, error) {
    url := fmt.Sprintf("%s/person?addr=%s", api, ADDRESS)
    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)
    }
 
    return nil, nil
}


当然运行go test也会返回一个错误,因为请求地址的问题,请求的并不是一个实际上的http server,那么自然也不会有正常的返回。

三、Golang httptest

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

person.go:

package person
 
import (
    "net/http"
    "fmt"
    "io/ioutil"
    "encoding/json"
 
    "github.com/astaxie/beego/logs"
)
 
const (
    ADDRESS = "shanghai"
)
 
type Person struct {
    Name string `json:"name"`
    Address string `json:"address"`
    Age int `json:"age"`
}
 
func GetInfo(api string) ([]Person, error) {
    url := fmt.Sprintf("%s/person?addr=%s", api, ADDRESS)
    resp, err := http.Get(url)
 
    defer resp.Body.Close()
 
    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)
    }
 
    bodyBytes, _ := ioutil.ReadAll(resp.Body)
    personList := make([]Person,0)
    err = json.Unmarshal(bodyBytes, &personList)
    if err != nil {
        logs.Error("decode data fail")
        return []Person{}, fmt.Errorf("decode data fail")
    }
 
    return personList, nil
}


person_test.go

package person
 
import (
    "testing"
    "net/http/httptest"
    "net/http"
    "fmt"
    "encoding/json"
)
 
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, _ := GetInfo(api)
 
    fmt.Println("reps:", resp)
}


解释一下:
>我们通过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和返回值来进行自己代码的测试了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Golang中,单元测试是开发人员在编写代码时编写的一种测试方法,它关注代码的局部而不是整体。单元测试的目的是验证代码的各个单元(函数、方法、类等)是否按照预期工作。单元测试通常比其他测试运行得更快,并且可以帮助开发人员在代码编写过程中及时发现和修复问题。\[1\] 在Golang中,我们可以使用testing包来编写和运行单元测试。在测试函数中,我们可以使用testing.T类型的参数来进行断言和错误报告。如果需要跳过某个测试,可以使用testing.T的Skip方法。例如,可以使用testing.Short()函数来判断是否在短模式下运行测试,并使用t.Skip方法跳过测试。\[2\] 要运行Golang单元测试,可以使用go test命令。默认情况下,go test会运行当前目录下的所有测试文件。可以使用-v选项来输出详细的日志信息。测试运行成功时,会显示PASS,测试失败时,会显示FAIL。如果只想测试单个文件,可以在go test命令后面加上被测试的原文件。如果只想测试单个方法,可以使用-run选项指定要运行的测试方法的名称。\[3\] 希望这些信息对你有帮助! #### 引用[.reference_title] - *1* *2* [Golang单元测试](https://blog.csdn.net/LinAndCurry/article/details/122324323)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Golang-单元测试](https://blog.csdn.net/ALEX_CYL/article/details/121793330)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值