Golang GIN写单测时,愉快的使用返回值

我写的接口,基本长这样:

{
    "code": 200,
    "msg": "原因",
    "result": {} // 或者空或者其它
}

所以在Go里,定义如下:

type Resp struct {
	Code   int         `json:"code"`
	Msg    string      `json:"msg"`
	Result interface{} `json:"result"`
}

然后,在返回值时,就可以定义好结构,比如:

type UserResult struct {
	UserID int `json:"user_id"`
}

然后把值塞进去。这样就愉快的返回了结果,不过,单测的时候可咋办呢?因为我想判断 result 里的值。最开始我尝试了如下方案:

func bodyToStruct(byteArray []byte, s interface{}) {
	err := json.Unmarshal(byteArray, &s)
	if err != nil {
		log.Printf("failed to unmarshal %s: %s", byteArray, err)
	}
}

这是一个把 []byte 转换成对应结构体的帮助函数,不过,result是不好转的,一开始我有尝试这样:

resp := Resp{Result: UserResult{}}

但是没有用,拿出来以后,Result 的值是一个map,于是我采用了曲线救国的方式,加一个工具函数,先把 Result 的值 marshal, 然后unmarshal到对应的结构体:

func resultToStruct(result, s interface{}) {
    byteArray, err := json.Marshal(result)
    if err != nil {
        logrus.Errorf("failed to marshal %s: %s", result, err)
        return
    }
 
    err = json.Unmarshal(byteArray, s)
    if err != nil {
        logrus.Errorf("failed to unmarshal %s: %s", byteArray, err)
        return
    }
}

很明显,有点low,转来转去。经过群友的提醒,是我最上面应该传指针,于是改成这样就可以了:

resp := Resp{Result: &Result}

这样就可以愉快的把值一次性通过 bodyToStruct 放到对应的结构体里,但是,要怎么样才能愉快的取值呢?因为即便做到了刚才那样, 如果你直接 resp.Result.UserID 还是不行,因为 Resp.Result 的定义是一个 interface{},解决方案如下:

resp := Resp{}
result := Result{}
resp.Result = &result

bodyToStruct(bytes, &resp)

于是就可以了。看看demo代码:

package main

import (
	"encoding/json"
	"log"
)

type Resp struct {
	Code   int         `json:"code"`
	Msg    string      `json:"msg"`
	Result interface{} `json:"result"`
}

type UserResult struct {
	UserID int `json:"user_id"`
}

func bodyToStruct(byteArray []byte, s interface{}) {
	err := json.Unmarshal(byteArray, &s)
	if err != nil {
		log.Printf("failed to unmarshal %s: %s", byteArray, err)
	}
}

func main() {
	byteArray := []byte(`{"code":200,"msg":"","result":{"user_id": 1}}`)

	resp := Resp{}
	result := UserResult{}
	resp.Result = &result

	bodyToStruct(byteArray, &resp)
	log.Printf("user id is %d", result.UserID)
}

于是,就终于可以愉快的类型安全的使用返回值了。这样做有几个好处:

  • 类型安全
  • 可以复用定义接口时写的struct
  • 可以补全

有同学可能认为,为啥不直接用interface,断言?嗯,其实我曾经年少轻狂的时候,经常说我是interface走天下,直到我的程序不断的 崩崩崩,我才老老实实重新做人,老老实实的类型安全。

这就是这次想要分享的技巧。


更多文章
  • Redis 5.0 Stream简明教程
  • 删除分区表
  • YouCompleteMe ycmd server SHUTDOWN 和 pyenv的问题
  • Docker组件介绍(二):shim, docker-init和docker-proxy
  • Docker组件介绍(一):runc和containerd
  • Prometheus MySQL Exporter源码阅读与分析
  • MySQL性能指标
  • 使用Dropbox来备份服务器文件
  • 《计算机网络-系统方法》读书笔记
  • Y Combinator《如何创业》笔记
  • Go类型嵌套
  • etcd源码阅读与分析(五):mvcc
  • etcd源码阅读与分析(四):lease
  • 干了这碗叔本华牌毒鸡汤 --- 《人生的智慧》
  • etcd源码阅读与分析(三):wal