前言
最近在做 leetcode 的题目,需要搞一些简单的测试代码,但是测试函数的输入输出的描述、判断函数是否成功比较麻烦
所以我就在想,能不能我定义一个 input 和一个 output,能够自动的将 input 转成 go 的代码,然后执行测试函数之后,比较返回值和 output 是否一致
要想这么做,肯定需要使用到反射,并且需要定义一下怎么从字符串转化到 go 的代码
parse 字符串定义
- 用逗号分隔数组的各个元素
- 用
\n
分隔多个输入或者多个输出 -
[1,2,3]
定义 slice -
{a: b}
定义 map - slice 或者 map 中的子元素的类型要看函数定义参数的类型
代码解析
parseParam 这里需要自定义 string 转化
参数是string
/ reflect.Type
,返回值是reflect.Value
主要是tring
to int
,bool
,slice
,map
,并转成reflect.Value
的类型
func parseParam(t *testing.T, param string, typ reflect.Type) reflect.Value {
var r reflect.Value
var as = assert.New(t)
param = strings.TrimSpace(param)
switch typ.Kind() {
case reflect.Int:
i, err := strconv.Atoi(param)
as.Nil(err)
r = reflect.ValueOf(i)
case reflect.String:
r = reflect.ValueOf(param)
case reflect.Bool:
b, err := strconv.ParseBool(param)
as.Nil(err)
r = reflect.ValueOf(b)
case reflect.Slice:
as.True(len(param) > 1)
as.True(strings.HasPrefix(param, "["))
as.True(strings.HasSuffix(param, "]"))
param = strings.TrimPrefix(param, "[")
param = strings.TrimSuffix(param, "]")
s2 := strings.Split(param, ",")
r = reflect.MakeSlice(reflect.SliceOf(typ.Elem()), 0, 0)
for _, v := range s2 {
r = reflect.Append(r, parseParam(t, v, typ.Elem()))
}
default:
panic(fmt.Sprintf("not support %s", typ.Kind()))
}
return r
}
输入参数解析
ft := reflect.TypeOf(Func)
fv := reflect.ValueOf(Func)
然后遍历 reflect.TypeOf(Func).In(i),这个是输入参数的类型
将输入参数字符串转成对应类型的 go 代码:ithParamIn := parseParam(t, input[i], ithCallInType)
var in []reflect.Value
for i := 0; i < ft.NumIn(); i++ {
ithCallInType := ft.In(i)
ithParamIn := parseParam(t, input[i], ithCallInType)
in = append(in, ithParamIn)
}
执行测试函数
out := fv.Call(in)
这边的 out 是一个[]reflect.Value,需要将他和函数签名,以及给定的输出的字符串进行比较
输出参数解析和验证
out 有三个,call 返回,ft.Out(i)的,output 的
for i := 0; i < ft.NumOut(); i++ {
ithCallRealOut := out[i] // 比input多的
ithCallOutType := ft.Out(i)
ithCallOut := parseParam(t, output[i], ithCallOutType)
as.Equal(ithCallOut.Kind(), ithCallRealOut.Kind())
as.Equal(ithCallOut.Kind(), ithCallRealOut.Convert(ithCallOutType).Kind())
as.Equal(ithCallOut.Interface(), ithCallRealOut.Convert(ithCallOutType).Interface())
}
所以看看这怎么用
可以看到只要给了 input 和 output 的字符串,和要测试的函数,那么就能通过反射自动解析参数,执行测试代码,并看看是不是给定的输出
<script src="https://gist.github.com/chyroc/8dc21c2ea65dc3c83e43a62b3f2759e8.js"></script>