单元测试是检测你写的一个函数是否具备安全性的一次检测。比如,当你写了一个函数,传入你期望的值,该函数是能正常执行的。但是当你传入一个非合理的值后,你写的函数直接宕机,而不是返回err。那就说明你写的函数存在逻辑漏洞。一个通过单元测试的函数,无论传入什么类型的值,返回的要么是结果,要么是error,绝对不会停止运行。
go test 命令会对遍历*_test.go文件中的符合上述规范的函数,生成一个临时的main包用于调用相应的测试函数,重构、运行、报告、测试,最后清除测试中的生成的临时文件。
// 用于对str字符串的子串截取 func subString(str string, start, end int) string { //将str转换为[]rune,支持Unicode编码 rs := []rune(str) length := len(rs) if start < 0 || start > length { panic("start is wrong") } if end < start || end > length { panic("end is wrong") } return string(rs[start:end]) }上述函数传入str、start、end参数,分别表示原始字符串,起始下标,结束下标。目的是为了获取str的子串。这里有个知识点,是关于go语言的编码的:
func TestCode(t *testing.T) { // []rune与Unicode str := "i am your 爸爸" fmt.Printf("str的原始长度:%d\n", len(str)) fmt.Printf("str的[]rune类型长度:%d\n", len([]rune(str))) }打印信息:
API server listening at: 127.0.0.1:64049 WARNING: undefined behavior - version of Delve is too old for Go version 1.19.0 (maximum supported version 1.18) === RUN TestCode str的原始长度:16 str的[]rune类型长度:12 --- PASS: TestCode (0.00s) PASS对[]rune的测试完毕,回到测试函数中:
func TestSubString(t *testing.T) { got := subString("hang", 0, 1) want := "h" if !reflect.DeepEqual(got, want) { t.Error("subString test is failed") } }打印信息:
API server listening at: 127.0.0.1:57824 WARNING: undefined behavior - version of Delve is too old for Go version 1.19.0 (maximum supported version 1.18) === RUN TestSubString --- PASS: TestSubString (0.00s) PASS在该测试函数中,got为函数返回信息;want为期望值。经过reflect.DeepEqual(got,want)函数进行匹配比较。
func TestSubString(t *testing.T) { type test struct { str string start int end int want interface{} } es := map[string]test{ "basic": {str: "basic", start: 0, end: 4, want: "basi"}, //basi "basicCN": {str: "你好world", start: 0, end: 1, want: "你"}, //"你" } for name, testValue := range es { t.Run(name, func(t *testing.T) { got := subString(testValue.str, testValue.start, testValue.end) if !reflect.DeepEqual(got, testValue.want) { t.Errorf("the name== %s got %v, want %v", t.Name(), got, testValue.want) } }) } } // 堆代码 duidaima.com API server listening at: 127.0.0.1:64621 WARNING: undefined behavior - version of Delve is too old for Go version 1.19.0 (maximum supported version 1.18) === RUN TestSubString === RUN TestSubString/basic === RUN TestSubString/basicCN --- PASS: TestSubString (0.00s) --- PASS: TestSubString/basic (0.00s) --- PASS: TestSubString/basicCN (0.00s) PASS
说明:在我写的函数SubString中,有 panic("start is wrong") 语句,这个结果在测试单元中是无法want的,即这种结果是无法测试。但是这个函数对我的项目是很重要的,未达到start和end就必须要panic。无需去深究,你可以将panic换成error.new("XXXX")。测试函数的部分知识就讲解完毕了,文章后续有案例实战。
func BenchmarkSubstring(b *testing.B) { for i := 0; i < b.N; i++ { subString("Benchmark", 0, 5) } } goos: windows goarch: amd64 pkg: sspaas.io/gpu-manage/utils/data cpu: Intel(R) Core(TM) i5-9300H CPU @ 2.40GHz BenchmarkSubstring BenchmarkSubstring-8 14777178 81.77 ns/op PASS相较于单元测试,基准测试使用得不是很多。
BenchmarkSubstring-8 14777178 81.77 ns/op
表示Substring函数在迭代14777178次下,平均每次消耗81.77 纳秒。b.N默认是很大的一个数,你也可以自定义迭代次数。一般是不要去动b.N基准测试还有很多知识点,包括内存消耗的检测、测试代码的覆盖率等。一般来说,作为开发人员仅仅测试一下迭代时间即可。若想了解更多,自行查阅资料。
func ExampleFunction() { result := YourFunction(input) fmt.Println(result) // Output: expected_output }闲的没事的人,才去写示例函数。有这时间,还不如去泡杯茶····
import ( "bytes" "crypto/aes" "crypto/cipher" "encoding/base64" ) // AesEncrypt AES加密 func AesEncrypt(orig string, key string) string { // 转换成字节数组 origData := []byte(orig) k := []byte(key) // 分组秘钥 block, _ := aes.NewCipher(k) // 获取秘钥块的长度 blockSize := block.BlockSize() // 补全码 origData = PKCS7Padding(origData, blockSize) // 加密模式 blockMode := cipher.NewCBCEncrypter(block, k[:blockSize]) // 创建数组 cryted := make([]byte, len(origData)) // 加密 blockMode.CryptBlocks(cryted, origData) return base64.StdEncoding.EncodeToString(cryted) } // AesDecrypt AES解密 func AesDecrypt(cryted string, key string) string { // 转成字节数组 crytedByte, _ := base64.StdEncoding.DecodeString(cryted) k := []byte(key) // 分组秘钥 block, _ := aes.NewCipher(k) // 获取秘钥块的长度 blockSize := block.BlockSize() // 加密模式 blockMode := cipher.NewCBCDecrypter(block, k[:blockSize]) // 创建数组 orig := make([]byte, len(crytedByte)) // 解密 blockMode.CryptBlocks(orig, crytedByte) // 去补全码 orig = PKCS7UnPadding(orig) return string(orig) } // PKCS7Padding 补码 func PKCS7Padding(ciphertext []byte, blocksize int) []byte { padding := blocksize - len(ciphertext)%blocksize padtext := bytes.Repeat([]byte{byte(padding)}, padding) return append(ciphertext, padtext...) } // PKCS7UnPadding 去码 func PKCS7UnPadding(origData []byte) []byte { length := len(origData) unpadding := int(origData[length-1]) return origData[:(length - unpadding)] } import ( "reflect" "testing" ) /*----------------------单元测试----------------------------------------*/ const key = "q+#@,6%ej-QP^mbc" func TestAesEncrypt(t *testing.T) { type test struct { org string key string want string } es := map[string]test{ "passed": {org: "basic", key: key, want: "2+1s9cqk1NzIUnq+G3VgEg=="}, //passed } for _, value := range es { got := AesEncrypt(value.org, value.key) if !reflect.DeepEqual(got, value.want) { t.Errorf("the name== %s got %v, want %v", t.Name(), got, value.want) } } } /* API server listening at: 127.0.0.1:54671 WARNING: undefined behavior - version of Delve is too old for Go version 1.19.0 (maximum supported version 1.18) === RUN TestAesEncrypt --- PASS: TestAesEncrypt (0.00s) PASS */ func BenchmarkAesEncrypt(b *testing.B) { for i := 0; i < b.N; i++ { AesEncrypt("test", key) } } /*pkg: sspaas.io/gpu-manage/utils/aes cpu: Intel(R) Core(TM) i5-9300H CPU @ 2.40GHz BenchmarkAesEncrypt BenchmarkAesEncrypt-8 1000000 1031 ns/op PASS */ func TestAesDecrypt(t *testing.T) { type test struct { org string key string want string } es := map[string]test{ "passed": {org: "2+1s9cqk1NzIUnq+G3VgEg==", key: key, want: "basic"}, //passed } for _, value := range es { got := AesDecrypt(value.org, value.key) if !reflect.DeepEqual(got, value.want) { t.Errorf("the name== %s got %v, want %v", t.Name(), got, value.want) } } } /*API server listening at: 127.0.0.1:54693 WARNING: undefined behavior - version of Delve is too old for Go version 1.19.0 (maximum supported version 1.18) === RUN TestAesDecrypt --- PASS: TestAesDecrypt (0.00s) PASS */ func BenchmarkAesDecrypt(b *testing.B) { for i := 0; i < b.N; i++ { AesDecrypt("2+1s9cqk1NzIUnq+G3VgEg==", key) } } /*pkg: sspaas.io/gpu-manage/utils/aes cpu: Intel(R) Core(TM) i5-9300H CPU @ 2.40GHz BenchmarkAesDecrypt BenchmarkAesDecrypt-8 1334907 883.0 ns/op PASS */总结