前言

最近在做 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>

代码(地址

<script src="https://gist.github.com/chyroc/3be97f3fc601a0a851e2bddbda48d89b.js"></script>