go中的自动测试

2018/05/18

前言

最近在做leetcode的题目,需要搞一些简单的测试代码,但是测试函数的输入输出的描述、判断函数是否成功比较麻烦

所以我就在想,能不能我定义一个input和一个output,能够自动的将input转成go的代码,然后执行测试函数之后,比较返回值和output是否一致

要想这么做,肯定需要使用到反射,并且需要定义一下怎么从字符串转化到go的代码

parse字符串定义

代码解析

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的字符串,和要测试的函数,那么就能通过反射自动解析参数,执行测试代码,并看看是不是给定的输出

代码(地址