Golang httptest包测试使用教程

 更新时间:2023年03月15日 10:39:05   作者:梦想画家  
这篇文章主要介绍了Golang httptest包测试使用,httptest包的理念是,非常容易模拟http服务,也就是说模拟响应写(response writer),提供给http处理器(handle),让我们测试http服务端和客户端很容易
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

当前首次学习到Golang httptest包时,着实打动了我。其他语言测试HTTP服务需要做很多工作或引用第三方工具,让人不可思议的是,Golang标准库就提供了非常容易理解的测试包。本文介绍httptest包的使用,为你Go http服务构建更好的端到端的测试。

httptest包的理念是,非常容易模拟http服务,也就是说模拟响应写(response writer),提供给http处理器(handle),让我们测试http服务端和客户端很容易。

本文主要介绍两个使用httptest的特定场景: 测试http server处理器,测试http客户端。

测试http服务端处理器

下面通过示例介绍http server的测试。首先看http服务程序,把请求字符串转为大写:

package main
import (
    "fmt"
    "log"
    "net/http"
    "net/url"
    "strings"
)
// Req: http://localhost:1234/upper?word=abc
// Res: ABC
func upperCaseHandler(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))
}
func main() {
    http.HandleFunc("/upper", upperCaseHandler)
    log.Fatal(http.ListenAndServe(":1234", nil))
}

现在想测试http server使用的upperCaseHandler逻辑,我们需要准备两方面:

  • 使用httptest.NewRequest暴露的函数创建http.Request对象,NewRequest返回Request, 可以传给http.Handler进行测试.
  • 使用httptest.NewRecorder函数创建http.ResponseWriter,返回httptest.ResponseRecorder。ResponseRecorder是

http.ResponseWriter 的实现,它记录变化为了后面测试检查.

httptest.ResponseRecorder

httptest.ResponseRecorder是 http.ResponseWriter 的实现,可以传给http server handle,记录所有处理并写回响应的数据,下面测试程序可以看到其如何实现:

package main
import (
    "io/ioutil"
    "net/http"
    "net/http/httptest"
    "testing"
)
func TestUpperCaseHandler(t *testing.T) {
    req := httptest.NewRequest(http.MethodGet, "/upper?word=abc", nil)
    w := httptest.NewRecorder()
    upperCaseHandler(w, req)
    res := w.Result()
    defer res.Body.Close()
    data, err := ioutil.ReadAll(res.Body)
    if err != nil {
        t.Errorf("expected error to be nil got %v", err)
    }
    if string(data) != "ABC" {
        t.Errorf("expected ABC got %v", string(data))
    }
}

上面示例中首先定义请求和响应,然后传入处理器进行测试。然后检查ResponseRecorder的Result方法输出:

func (rw *ResponseRecorder) Result() *http.Response

Result返回处理器生成的响应。返回相应至少有StatusCode, Header, Body, 以及可选其他内容,未来可能会填充更多字段,所以调用者在测试中不应该深度比较相等。

测试HTTP客户端

测试服务端处理器相对容易,特别当测试处理器逻辑时,仅需要在测试中模拟http.ResponseWriter 和 http.Request对象。对于HTTP客户端测试,情况稍晚有点复杂。原因是有时不容易模拟或复制整个HTTP Server,请看下面示例:

package main
import (
    "io/ioutil"
    "net/http"
    "github.com/pkg/errors"
)
type Client struct {
    url string
}
func NewClient(url string) Client {
    return Client{url}
}
func (c Client) UpperCase(word string) (string, error) {
    res, err := http.Get(c.url + "/upper?word=" + word)
    if err != nil {
        return "", errors.Wrap(err, "unable to complete Get request")
    }
    defer res.Body.Close()
    out, err := ioutil.ReadAll(res.Body)
    if err != nil {
        return "", errors.Wrap(err, "unable to read response data")
    }
    return string(out), nil
}

client需要url,表示远程服务端基地址。然后调用/upper,带上输入单词,最后返回结果字符串给调用者,如果调用不成功还返回错误对象。为了测试这段代码,需要模拟整个http服务端逻辑,或至少是响应请求路径:/upper。使用httptest包可以模拟整个http 服务,通过初始化本地服务,监听回环地址并返回你想要的任何内容。

使用 httptest.Server

通过调用httptest.NewServer函数生成我们想要的 httptest.Server。表示http服务,监听回环地址及可选的端口号,用于实现端到端HTTP测试。

func NewServer(handler http.Handler) *Server

NewServer 启动并返回新的HTTP服务,调用者使用完成后应该调用Close方法结束服务。下面通过示例进行解释:

package main
import (
    "fmt"
    "net/http"
    "net/http/httptest"
    "strings"
    "testing"
)
func TestClientUpperCase(t *testing.T) {
    expected := "dummy data"
    svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, expected)
    }))
    defer svr.Close()
    c := NewClient(svr.URL)
    res, err := c.UpperCase("anything")
    if err != nil {
        t.Errorf("expected err to be nil got %v", err)
    }
    // res: expected\r\n
    // due to the http protocol cleanup response
    res = strings.TrimSpace(res)
    if res != expected {
        t.Errorf("expected res to be %s got %s", expected, res)
    }
}

上面示例中使用httptest.NewServer函数创建了模拟http服务器,给它传入自定义模拟处理器,总是返回相同的数据。并使用服务端url作为客户端请求url,从而模拟并让服务端返回任何我们想测试的内容。

当然我们可以修改处理器,让其返回我们期望的逻辑:

func TestClientUpperCase(t *testing.T) {
	svr := httptest.NewServer(http.HandlerFunc(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 {
			fmt.Fprintf(w, strings.ToUpper(word))
		} else {
			fmt.Fprintf(w, "no input")
		}
	}))
	defer svr.Close()
	expected := "ANYTHING"
	c := NewClient(svr.URL)
	res, err := c.UpperCase("anything")
	if err != nil {
		t.Errorf("expected err to be nil got %v", err)
	}
	// res: expected\r\n
	// due to the http protocol cleanup response
	res = strings.TrimSpace(res)
	if res != expected {
		t.Errorf("expected res to be %s got %s", expected, res)
	}
}

总结

本文介绍httptest包,可以很方便测试http服务端处理逻辑,以及模拟http服务端测试客户端请求逻辑。由于很方面模拟,从而可以把一组参数和期望值进行组合,循环进行测试并对比结果,可以极大地提升测试效率。

到此这篇关于Golang httptest包测试使用教程的文章就介绍到这了,更多相关Go httptest内容请搜索程序员之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持程序员之家!

相关文章

  • Golang 定时器(Timer 和 Ticker),这篇文章就够了

    Golang 定时器(Timer 和 Ticker),这篇文章就够了

    这篇文章主要介绍了Golang 定时器(Timer 和 Ticker),这篇文章就够了,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • Go语言并发之Sync包的6个关键概念总结

    Go语言并发之Sync包的6个关键概念总结

    这篇文章主要为大家详细介绍了Go语言并发中Sync包的6个关键概念,文中的示例代码讲解详细,对我们深入学习Go语言有一定的帮助,需要的可以参考一下
    2023-05-05
  • GoLang bytes.Buffer基础使用方法详解

    GoLang bytes.Buffer基础使用方法详解

    Go标准库中的bytes.Buffer(下文用Buffer表示)类似于一个FIFO的队列,它是一个流式字节缓冲区,我们可以持续向Buffer尾部写入数据,从Buffer头部读取数据。当Buffer内部空间不足以满足写入数据的大小时,会自动扩容
    2023-03-03
  • Go语言基础知识总结(语法、变量、数值类型、表达式、控制结构等)

    Go语言基础知识总结(语法、变量、数值类型、表达式、控制结构等)

    这篇文章主要介绍了Go语言基础知识总结(语法、变量、数值类型、表达式、控制结构等),本文汇总了Go语言的入门知识,需要的朋友可以参考下
    2014-10-10
  • GO语言的控制语句详解包括GO语言的指针语法

    GO语言的控制语句详解包括GO语言的指针语法

    这篇文章主要介绍了GO语言的控制语句详解包括GO语言的指针语法,GO语言switch结构,GO语言for的4种结构需要的朋友可以参考下
    2022-12-12
  • Golang使用Swag搭建api文档的全过程

    Golang使用Swag搭建api文档的全过程

    Gin是Golang目前最为常用的Web框架之一,公司项目验收需要API接口设计说明书(Golang后端服务基于Gin框架编写),所以本文给大家介绍了Golang使用Swag搭建api文档的全过程,需要的朋友可以参考下
    2024-02-02
  • Golang?Redis连接池实现原理及示例探究

    Golang?Redis连接池实现原理及示例探究

    这篇文章主要为大家介绍了Golang?Redis连接池实现示例探究,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • go语言如何导入和使用包示例详解

    go语言如何导入和使用包示例详解

    这篇文章主要为大家介绍了go语言如何导入和使用包示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • Golang递归获取目录下所有文件方法实例

    Golang递归获取目录下所有文件方法实例

    这篇文章主要给大家介绍了关于Golang递归获取目录下所有文件的相关资料,文中通过实例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2023-02-02
  • GoFrame基于性能测试得知grpool使用场景

    GoFrame基于性能测试得知grpool使用场景

    这篇文章主要为大家介绍了GoFrame基于性能测试得知grpool使用场景示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06

最新评论

?


http://www.vxiaotou.com