Golang 反射 && 单元测试
一.反射
1 package reflecttest 2 3 import ( 4 "fmt" 5 "reflect" 6 ) 7 8 // 语法:const [name] [type] = [value] 9 // const 只能修饰int\float\string\bool作为常量 10 const ss string = "123" 11 const ( 12 a = iota // a是0,往后依次递增1 13 b 14 c 15 d 16 ) 17 18 /* 19 反射: 20 1.反射可以在运行时动态获取变量的各种信息,比如变量的类型(type),类别(kind) 21 2.如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法) 22 3.通过反射,可以修改变量的值,可以调用关联的方法 23 4.使用反射,需要import("reflect") 24 5.变量、interface{}、reflect.Value时可以相互转换的。 25 */ 26 27 func TestReflect(b interface{}) { 28 // 1.将interface{}专成reflect.Value 29 rVal := reflect.ValueOf(b) 30 31 // 2.将reflect.Value转成interface{} 32 iVal := rVal.Interface() 33 34 // 3.获取kind常量 35 fmt.Printf("kind=%v, type=%T\n", rVal.Kind(), rVal.Kind()) // reflect.Kind 36 37 // 4.将interface{}专程原来的变量类型 38 switch iVal.(type) { 39 case int: 40 fmt.Println("int") 41 // 4.转reflect.Type 42 reflect.ValueOf(iVal.(int)) // 返回的就是:reflect.Type 43 rVal.Type() // 返回的就是:reflect.Type 44 fmt.Printf("kind=%v\n", rVal.Type().Kind()) 45 default: 46 fmt.Println("err type!") 47 } 48 49 } 50 51 func TestStructReflect() { 52 var ss string = "Tom" 53 TestElem := func(b interface{}) { 54 // 1.读取reflectValue 55 rVal := reflect.ValueOf(b) 56 // 2.Kind 57 fmt.Println("Kind=", rVal.Kind()) 58 // 3.设置值. 59 // 通过反射来修改变量,注意当使用SetXxx方法来设置时,需要通过对应的指针类型来完成,这样才能改变传入变量的值,同时需要使用到reflect.Value.Elem()方法 60 // 因为rVal是指针,我们的目的是修改指针指向的内容.这里rVal.Elem()的作用是,将rVal包装成可修改指针指向内容值的reflect.Value返回 61 rVal.Elem().SetString("123456789") 62 // rVal.SetInt(1) 63 } 64 fmt.Println(&ss) 65 TestElem(&ss) 66 fmt.Println(&ss) 67 fmt.Println(ss) 68 }
1 package reflecttest 2 3 import ( 4 "fmt" 5 "reflect" 6 ) 7 8 type Monster struct { 9 Name string `json:"name"` 10 Age int `json:"age"` 11 Score float32 `json:"score"` 12 Sex string 13 } 14 15 /* 显示值 */ 16 func (self *Monster) Print() { 17 fmt.Println("-----start--------") 18 fmt.Println(*self) 19 fmt.Println("-----end--------") 20 } 21 22 /* 返回两个数的和 */ 23 func (self *Monster) GetSum(a int, b int) int { 24 return a + b 25 } 26 27 /* 赋值 */ 28 func (self *Monster) SetValue(name string, age int, score float32, sex string) { 29 self.Name = name 30 self.Age = age 31 self.Score = score 32 self.Sex = sex 33 } 34 35 // 反射字段标签的用法 36 func TestStructFieldTag(b interface{}) { 37 // 获取reflect.Type 38 typ := reflect.TypeOf(b) 39 typ1 := reflect.TypeOf(b) 40 // 获取reflect.Value 41 val := reflect.ValueOf(b) 42 val1 := reflect.ValueOf(b) 43 // 获取对应的类型 44 kd := val.Kind() 45 if kd == reflect.Ptr { 46 val = val.Elem() 47 kd = val.Kind() 48 typ = val.Type() 49 } 50 // 如果不是结构体就退出 51 if kd != reflect.Struct { 52 fmt.Println("传入的值必须是struct类型.") 53 return 54 } 55 56 // 获取该结构体有几个字段 57 num := val.NumField() 58 fmt.Printf("%v 结构体有%v个字段.\n", typ, num) 59 // 获取每个字段的值,以及字段设定的标签 60 for i := 0; i < num; i++ { 61 fmt.Printf("字段%v,值=%v\n", typ.Field(i).Name, val.Field(i)) 62 // 注意:获取字段标签值,需要通过reflect.Type来获取 63 tagVal := typ.Field(i).Tag.Get("json") 64 if tagVal != "" { 65 fmt.Printf("字段'%v',标签=%v\n", typ.Field(i).Name, tagVal) 66 } 67 if typ.Field(i).Name == "Name" { 68 // 修改字段值 69 val.Field(i).SetString("ReName") 70 } 71 } 72 73 // 获取该结构体有多少个方法 74 num = val.NumMethod() 75 fmt.Printf("%v 结构体有%v个方法.\n", typ, num) 76 num = typ1.NumMethod() 77 fmt.Printf("%v 结构体有%v个方法.\n", typ1, num) 78 79 // 调用结构体的方法,并传参数 80 var params []reflect.Value 81 // reflecttest.Monster 结构体有0个方法. 82 params = append(params, reflect.ValueOf(1)) 83 // *reflecttest.Monster 结构体有3个方法. 84 params = append(params, reflect.ValueOf(2)) 85 res := val1.MethodByName("GetSum").Call(params) 86 // 调用结构体中的'GetSum()'=3 , 返回的类型是[]reflect.Value 87 fmt.Printf("调用结构体中的'GetSum()'=%v , 返回的类型是%T\n", res[0], res) 88 89 } 90 91 // 函数适配器:给要执行的函数加日志 92 func TestReflectFunc(function, f1 interface{}) { 93 94 funVal := reflect.ValueOf(function).Elem() 95 funTyp := funVal.Type() 96 97 packFunc := func(in []reflect.Value) (result []reflect.Value) { 98 fmt.Println("---begin---") 99 result = reflect.ValueOf(f1).Call(in) 100 fmt.Println("---end---") 101 return result 102 } 103 104 v := reflect.MakeFunc(funTyp, packFunc) 105 funVal.Set(v) 106 107 } 108 109 func TestReflect1() { 110 tfunc := func(a int, b int) int { 111 return a + b 112 } 113 114 TestReflectFunc(&tfunc, tfunc) 115 c := tfunc(1, 2) 116 fmt.Println(c) 117 118 obj := Monster{} 119 obj.SetValue("Tom", 19, 102, "Man") 120 TestStructFieldTag(&obj) 121 fmt.Println(obj) 122 }
二.单元测试
1 package main 2 3 import ( 4 "testing" 5 ) 6 7 func TestAddUpper(t *testing.T) { 8 /* 9 注意细节: 10 1.测试文件名必须"_test.go"结尾.一般用测试单元文件的名加上后缀 11 2.测试用例函数必须以Test开头,一般用"Test+被测试的函数名" 12 3.Testddpper(t *testing.T) 的形参类型必须是"t *testing.T" 13 4.一个测试用例文件中可以有多个测试用例函数 14 5.运行测试用例指令 15 go test // 如果运行正确,无日志,错误时,会输出日志 16 go test -v // 运行正确错误,都输出日志 17 go test [测试单元文件名main_test.go] [测试的原文件main.go] // 测试指定的测试单元 18 go test -v -test.run [具体测试的方法TestAddUpper] // 测试具体单个方法 19 6.当出现错误时,可以使用t.Fatalf 来格式化输出错误信息,并退出程序 20 7.t.Logf方法可以输出相应的日志 21 8.测试用例函数,并没有放在main函数中也执行了,这就是测试用例的方便之处 22 9,PASS表示测试用例运行成功,FAIL表示测试用例运行失败 23 24 */ 25 sum := TestUnit(10) 26 if sum != 55 { 27 t.Fatalf("TestUnit(10) 执行错误,期望值=%v,实际值=%v\n", 55, sum) 28 } 29 t.Logf("TestUnit(10) 执行正确") 30 }