Go 单元测试之HTTP请求与API测试

这篇具有很好参考价值的文章主要介绍了Go 单元测试之HTTP请求与API测试。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录
  • 一、httptest
    • 1.1 前置代码准备
    • 1.2 介绍
    • 1.3 基本用法
  • 二、gock
    • 2.1介绍
    • 2.2 安装
    • 2.3 基本使用
    • 2.4 举个例子
      • 2.4.1 前置代码
      • 2.4.2 测试用例

一、httptest

1.1 前置代码准备

假设我们的业务逻辑是搭建一个http server端,对外提供HTTP服务。用来处理用户登录请求,用户需要输入邮箱,密码。

package main

import (
	regexp "github.com/dlclark/regexp2"
	"github.com/gin-gonic/gin"
	"net/http"
)

type UserHandler struct {
	emailExp    *regexp.Regexp
	passwordExp *regexp.Regexp
}

func (u *UserHandler) RegisterRoutes(server *gin.Engine) {
	ug := server.Group("/user")
	ug.POST("/login", u.Login)
}
func NewUserHandler() *UserHandler {
	const (
		emailRegexPattern    = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$"
		passwordRegexPattern = `^(?=.*[A-Za-z])(?=.*\d)(?=.*[$@$!%*#?&])[A-Za-z\d$@$!%*#?&]{8,}$`
	)
	emailExp := regexp.MustCompile(emailRegexPattern, regexp.None)
	passwordExp := regexp.MustCompile(passwordRegexPattern, regexp.None)
	return &UserHandler{
		emailExp:    emailExp,
		passwordExp: passwordExp,
	}
}

type LoginRequest struct {
	Email string `json:"email"`
	Pwd   string `json:"pwd"`
}

func (u *UserHandler) Login(ctx *gin.Context) {
	var req LoginRequest
	if err := ctx.ShouldBindJSON(&req); err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{"msg": "参数不正确!"})
		return
	}

	// 校验邮箱和密码是否为空
	if req.Email == "" || req.Pwd == "" {
		ctx.JSON(http.StatusBadRequest, gin.H{"msg": "邮箱或密码不能为空"})
		return
	}

	// 正则校验邮箱
	ok, err := u.emailExp.MatchString(req.Email)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{"msg": "系统错误!"})
		return
	}
	if !ok {
		ctx.JSON(http.StatusBadRequest, gin.H{"msg": "邮箱格式不正确"})
		return
	}

	// 校验密码格式
	ok, err = u.passwordExp.MatchString(req.Pwd)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{"msg": "系统错误!"})
		return
	}
	if !ok {
		ctx.JSON(http.StatusBadRequest, gin.H{"msg": "密码必须大于8位,包含数字、特殊字符"})
		return
	}

	// 校验邮箱和密码是否匹配特定的值来确定登录成功与否
	if req.Email != "123@qq.com" || req.Pwd != "hello#world123" {
		ctx.JSON(http.StatusBadRequest, gin.H{"msg": "邮箱或密码不匹配!"})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{"msg": "登录成功!"})
}

func InitWebServer(userHandler *UserHandler) *gin.Engine {
	server := gin.Default()
	userHandler.RegisterRoutes(server)
	return server
}

func main() {
	uh := &UserHandler{}
	server := InitWebServer(uh)
	server.Run(":8080") // 在8080端口启动服务器
}

1.2 介绍

在 Web 开发场景下,单元测试经常需要模拟 HTTP 请求和响应。使用 httptest 可以让我们在测试代码中创建一个 HTTP 服务器实例,并定义特定的请求和响应行为,从而模拟真实世界的网络交互,在Go语言中,一般都推荐使用Go标准库 net/http/httptest 进行测试。

1.3 基本用法

使用 httptest 的基本步骤如下:

  1. 导入 net/http/httptest 包。
  2. 创建一个 httptest.Server 实例,并指定你想要的服务器行为。
  3. 在测试代码中使用 httptest.NewRequest 创建一个模拟的 HTTP 请求,并将其发送到 httptest.Server
  4. 检查响应内容或状态码是否符合预期。

以下是一个简单的 httptest 用法示例

package main

import (
	"bytes"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestUserHandler_Login(t *testing.T) {
	// 定义测试用例
	testCases := []struct {
		name     string
		reqBody  string
		wantCode int
		wantBody string
	}{
		{
			name:     "登录成功",
			reqBody:  `{"email": "123@qq.com", "pwd": "hello#world123"}`,
			wantCode: http.StatusOK,
			wantBody: `{"msg": "登录成功!"}`,
		},
		{
			name:     "参数不正确",
			reqBody:  `{"email": "123@qq.com", "pwd": "hello#world123",}`,
			wantCode: http.StatusBadRequest,
			wantBody: `{"msg": "参数不正确!"}`,
		},
		{
			name:     "邮箱或密码为空",
			reqBody:  `{"email": "", "pwd": ""}`,
			wantCode: http.StatusBadRequest,
			wantBody: `{"msg": "邮箱或密码不能为空"}`,
		},
		{
			name:     "邮箱格式不正确",
			reqBody:  `{"email": "invalidemail", "pwd": "hello#world123"}`,
			wantCode: http.StatusBadRequest,
			wantBody: `{"msg": "邮箱格式不正确"}`,
		},
		{
			name:     "密码格式不正确",
			reqBody:  `{"email": "123@qq.com", "pwd": "invalidpassword"}`,
			wantCode: http.StatusBadRequest,
			wantBody: `{"msg": "密码必须大于8位,包含数字、特殊字符"}`,
		},
		{
			name:     "邮箱或密码不匹配",
			reqBody:  `{"email": "123123@qq.com", "pwd": "hello#world123"}`,
			wantCode: http.StatusBadRequest,
			wantBody: `{"msg": "邮箱或密码不匹配!"}`,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			// 创建一个 gin 的上下文
			server := gin.Default()
			h := NewUserHandler()
			h.RegisterRoutes(server)
			// mock 创建一个 http 请求
			req, err := http.NewRequest(
				http.MethodPost,                     // 请求方法
				"/user/login",                       // 请求路径
				bytes.NewBuffer([]byte(tc.reqBody)), // 请求体
			)
			// 断言没有错误
			assert.NoError(t, err)
			// 设置请求头
			req.Header.Set("Content-Type", "application/json")
			// 创建一个响应
			resp := httptest.NewRecorder()
			// 服务端处理请求
			server.ServeHTTP(resp, req)
			// 断言响应码和响应体
			assert.Equal(t, tc.wantCode, resp.Code)
			// 断言 JSON 字符串是否相等
			assert.JSONEq(t, tc.wantBody, resp.Body.String())
		})
	}
}

在这个例子中,我们创建了一个简单的 HTTP 请求,TestUserHandler_Login 函数定义了一个测试函数,用于测试用户登录功能的不同情况。

  1. testCases 列表定义了多个测试用例,每个测试用例包含了测试名称、请求体、期望的 HTTP 状态码和期望的响应体内容。
  2. 使用 for 循环遍历测试用例列表,每次循环创建一个新的测试子函数,并在其中模拟 HTTP 请求发送给登录接口。
  3. 在每个测试子函数中,先创建一个 Gin 的默认上下文和用户处理器 UserHandler,然后注册路由并创建一个模拟的 HTTP 请求。
  4. 通过 httptest.NewRecorder() 创建一个响应记录器,使用 server.ServeHTTP(resp, req) 处理模拟请求,得到响应结果。
  5. 最后使用断言来验证实际响应的 HTTP 状态码和响应体是否与测试用例中的期望一致。

最后,使用Goland 运行测试,结果如下:

Go 单元测试之HTTP请求与API测试

二、gock

2.1介绍

gock 可以帮助你在测试过程中模拟 HTTP 请求和响应,这对于测试涉及外部 API 调用的应用程序非常有用。它可以让你轻松地定义模拟请求,并验证你的应用程序是否正确处理了这些请求。

GitHub 地址:github.com/h2non/gock

2.2 安装

你可以通过以下方式安装 gock:

go get -u github.com/h2non/gock

导入 gock 包:

import "github.com/h2non/gock"

2.3 基本使用

gock 的基本用法如下:

  1. 启动拦截器:在测试开始前,使用 gock.New 函数启动拦截器,并指定你想要拦截的域名和端口。
  2. 定义拦截规则:你可以使用 gock.Intercept 方法来定义拦截规则,比如拦截特定的 URL、方法、头部信息等。
  3. 设置响应:你可以使用 gock.NewJsongock.NewText 等方法来设置拦截后的响应内容。
  4. 运行测试:在定义了拦截规则和响应后,你可以运行测试,gock 会拦截你的 HTTP 请求,并返回你设置的响应。

2.4 举个例子

2.4.1 前置代码

如果我们是在代码中请求外部API的场景(比如通过API调用其他服务获取返回值)又该怎么编写单元测试呢?

例如,我们有以下业务逻辑代码,依赖外部API:http://your-api.com/post提供的数据。

// ReqParam API请求参数
type ReqParam struct {
	X int `json:"x"`
}

// Result API返回结果
type Result struct {
	Value int `json:"value"`
}

func GetResultByAPI(x, y int) int {
	p := &ReqParam{X: x}
	b, _ := json.Marshal(p)

	// 调用其他服务的API
	resp, err := http.Post(
		"http://your-api.com/post",
		"application/json",
		bytes.NewBuffer(b),
	)
	if err != nil {
		return -1
	}
	body, _ := ioutil.ReadAll(resp.Body)
	var ret Result
	if err := json.Unmarshal(body, &ret); err != nil {
		return -1
	}
	// 这里是对API返回的数据做一些逻辑处理
	return ret.Value + y
}

在对类似上述这类业务代码编写单元测试的时候,如果不想在测试过程中真正去发送请求或者依赖的外部接口还没有开发完成时,我们可以在单元测试中对依赖的API进行mock。

2.4.2 测试用例

使用gock对外部API进行mock,即mock指定参数返回约定好的响应内容。 下面的代码中mock了两组数据,组成了两个测试用例。文章来源地址https://www.toymoban.com/news/detail-854658.html

package gock_demo

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"gopkg.in/h2non/gock.v1"
)

func TestGetResultByAPI(t *testing.T) {
	defer gock.Off() // 测试执行后刷新挂起的mock

	// mock 请求外部api时传参x=1返回100
	gock.New("http://your-api.com").
		Post("/post").
		MatchType("json").
		JSON(map[string]int{"x": 1}).
		Reply(200).
		JSON(map[string]int{"value": 100})

	// 调用我们的业务函数
	res := GetResultByAPI(1, 1)
	// 校验返回结果是否符合预期
	assert.Equal(t, res, 101)

	// mock 请求外部api时传参x=2返回200
	gock.New("http://your-api.com").
		Post("/post").
		MatchType("json").
		JSON(map[string]int{"x": 2}).
		Reply(200).
		JSON(map[string]int{"value": 200})

	// 调用我们的业务函数
	res = GetResultByAPI(2, 2)
	// 校验返回结果是否符合预期
	assert.Equal(t, res, 202)

	assert.True(t, gock.IsDone()) // 断言mock被触发
}

到了这里,关于Go 单元测试之HTTP请求与API测试的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Go 的 Http 请求系统指南

    前几天在 “知乎想法” 谈到了一个话题,如何模仿学习,举了通过 net/http client 模仿 Pyhton 的requests的例子。但并未实践,难道想法真的只能是想法吗?当然不是,于是我决定先暂停一周 GO 笔记,来实践下自己的想法。 有些新的知识,我们可以通过模仿学习 本文将通过 GO 实

    2024年01月23日
    浏览(36)
  • Go语言测试——【单元测试 | Mock测试 | 基准测试】

    作者 :非妃是公主 专栏 :《Golang》 博客主页 :https://blog.csdn.net/myf_666 个性签:顺境不惰,逆境不馁,以心制境,万事可成。——曾国藩 软件测试 :软件测试(英语:Software Testing),描述一种用来促进鉴定软件的正确性、完整性、安全性和质量的过程。换句话说,软件测

    2024年02月10日
    浏览(59)
  • Go 单元测试之mock接口测试

    目录 一、gomock 工具介绍 二、安装 三、使用 3.1 指定三个参数 3.2 使用命令为接口生成 mock 实现 3.3 使用make 命令封装处理mock 四、接口单元测试步骤 三、小黄书Service层单元测试 四、flags 五、打桩(stub) 参数 六、总结 6.1 测试用例定义 6.2 设计测试用例 6.3 执行测试用例代码

    2024年04月22日
    浏览(37)
  • Go语言单元测试

    Go语言中的测试依赖 go test 命令,go test 命令是一个按照一定约定和组织的测试代码的驱动程序。在包目录 内,所有以 _test.go 为后缀名的源代码文件都是 go test 测试的一部分,不会被 go build 编译到最终的可执行 文件中。 在 *_test.go 文件中有三种类型的函数, 单元测试函数

    2024年02月11日
    浏览(49)
  • GO——单元测试(test)

    go test用来做什么 做单元测试,测试函数是否符合预期 go test在哪个包 testing 如何使用 参考: https://geektutu.com/post/quick-go-test.html 以 my_func.go 中的Add方法为例 在同一个文件夹下添加my_func_test.go文件 测试文件以_test.go为结尾 里面的测试方法以Test开头,但是不一定是要跟方法名,

    2024年01月20日
    浏览(50)
  • Go 单元测试基本介绍

    目录 一、单元测试基本介绍 1.1 什么是单元测试? 1.2 如何写好单元测试 1.3 单元测试的优点 1.4 单元测试的设计原则 二、Go语言测试 2.1 Go单元测试概要 2.2 Go单元测试基本规范 2.3 一个简单例子 2.3.1 使用Goland 生成测试文件 2.3.2 运行单元测试 2.3.3 完善测试用例 2.3.5 回归测试

    2024年04月16日
    浏览(42)
  • Go单元测试入门

    测试文件以 _test.go 结尾 假如 calc.go 的代码如下: 那么 calc_test.go 中的测试用例可以这么写: 测试用例名称一般 命名为 Test 加上待测试的方法名。 测试用的参数有且只有一个,在这里是 t *testing.T。 基准测试(benchmark)的参数是 *testing.B,TestMain 的参数是 *testing.M 类型。 运行

    2024年02月09日
    浏览(41)
  • 使用Go发送HTTP GET请求

    在Go语言中,我们可以使用 net/http 包来发送HTTP GET请求。以下是一个简单的示例,展示了如何使用Go发送HTTP GET请求并获取响应。 go 复制代码 package  main import  ( \\\"fmt\\\"   \\\"io/ioutil\\\"   \\\"net/http\\\"   ) func   main ()  { // 创建一个HTTP客户端 client := http.Client{} // 创建一个GET请求 req, err :=

    2024年01月23日
    浏览(45)
  • 使用Go发送HTTP POST请求

    在Go语言中,我们可以使用 net/http 包来发送HTTP POST请求。以下是一个简单的示例,展示了如何使用Go发送HTTP POST请求并获取响应。 go 复制代码 package  main import  ( \\\"bytes\\\"   \\\"fmt\\\"   \\\"io/ioutil\\\"   \\\"net/http\\\"   ) func   main ()  { // 创建一个HTTP客户端 client := http.Client{} // 创建一个POST请求

    2024年01月23日
    浏览(46)
  • Go语言的单元测试与基准测试详解

    单元测试 以一个加法函数为例,对其进行单元测试。 首先编写add.go文件: 其次编写add_test.go文件,在go语言中,测试文件均已_test结尾,这里只需要在被测试的文件后加上_test即可。并且测试文件与要被测试的文件需要放在同一个包中,并不像 Java 那样需要将所有的测试文件

    2024年02月03日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包