Go测试框架-Mock http请求

2022-06-30 10:26:06 浏览数 (1)

一. 使用httptest来mock一些http的服务

1.1 背景说明

最近做了一个营销活动,开发礼包领取功能时依赖了外部的系统发放点券,已知点券发放接口是非常稳定ok的了,目前这个接口仅可以在生产被调用。为了测试礼包领取功能,需要mock掉依赖的外部点券发放接口。

1.2 操作实践

代码语言:txt复制
// 这是配置文件里面的,为了演示这里写死
// RemoteGamePointsApi 点券api地址
var RemoteGamePointsApi = "/gift_pack"

// RemoteGamePointsServiceAddress 点券服务地址
var RemoteGamePointsServiceAddress = ""

// GiftPackResp 外部点券发放接口响应
type GiftPackResp struct {
	QQ         string
	GamePoints int64
}

// GiftPackGet 礼包领取功能
func GiftPackGet(qq string) error {
	// step1. 参数校验

	// step2. 检查用户是否在本次活动黑名单 ...

	// step3.检查用户是否已经领取过了 ...

	// step4. 锁定当前账号, 防止同一个时刻同一个账号同时发放  ...

	// step5. 调用点券发放接口
	return sendGamePoints(qq)
}

// SendGamePoints 调用外部接口发送游戏点券
func sendGamePoints(qq string) error {
	if !qqValidator(qq) {
		return errors.New("qq is invalid")
	}

	// 发送点券
	resp, err := http.Get(fmt.Sprintf("%s/%s?qq=%s", RemoteGamePointsServiceAddress, RemoteGamePointsApi, qq))
	defer resp.Body.Close()

	// 解析响应结果
	bs, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	ret := GiftPackResp{}
	err = json.Unmarshal(bs, &ret)
	if err != nil {
		return err
	}
	fmt.Println(ret)
	return err

}

// qq 合法性校验
func qqValidator(qq string) bool {
	reg := regexp.MustCompile(`^[1-9]d{3,10}$`)
	return reg.MatchString(qq)
}

// Test_GiftPackGet 测试礼包发放接口
func Test_GiftPackGet(t *testing.T) {
	Convey("test utils GiftPackGet", t, func() {
		Convey("When request use httptest mock remote api", func() {
			ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				if r.Method != http.MethodGet {
					w.WriteHeader(http.StatusNotFound)
				}
				if r.URL.EscapedPath() != RemoteGamePointsApi {
					w.WriteHeader(http.StatusNotFound)
				}
				qq := r.URL.Query().Get("qq")
				w.WriteHeader(http.StatusOK)
				g := GiftPackResp{qq, 100}
				bs, _ := json.Marshal(g)
				w.Write(bs)
			}))

			defer ts.Close()
			RemoteGamePointsServiceAddress = ts.URL
			err := GiftPackGet("3095764312")
			So(err, ShouldBeNil)
		})
	})
}

代码解释:

  • GiftPackGet 是对外的礼包发放接口,在调用前需要做一些活动规则的检查
  • sendGamePoints 是调用的外部的点券发送接口api,调用会返回点券发送的回执
  • 通过mock掉点券发送接口api, 我们可以将测试聚焦在具体的活动规则检查上,这部分才是我们自己的业务逻辑,是我们需要重点测试的部分。

二. 关于mock的思考

关于mock一直都是一个讨论比较激烈的点:

一方的人主张不要滥用mock,能不mock就不mock。被测单元也不一定是具体的一个函数,可能是多个函数本来就应该串起来,必要的时候再mock。

一方则主张将被测函数所有调用的外面函数全部mock掉,只关注被测函数自己的一行行代码,只要调用其他函数,全都mock掉,用假数据来测试。

个人感觉用不用mock就是一个选择问题,需要具体问题具体看待。如果依赖的模块自己可控, 那么我宁愿用测试数据保证上下依赖ok,专注到被测试的模块。

三. 什么时候适合mock?

  • 该对象提供非确定的结果(比如当前的时间或者当前的温度)。
  • 对象的某些状态难以创建或者重现(比如网络错误或者文件读写错误)。
  • 对象方法上的执行太慢(比如在测试开始之前初始化数据库)。
  • 该对象还不存在或者其行为可能发生变化(比如测试驱动开发中驱动创建新的类)。
  • 该对象必须包含一些专门为测试准备的数据或者方法。
  • 外部的一些调用代价较高的接口(例如调用一次100元的接口)。

0 人点赞