通过测试学习go语言之迭代
一、 完成迭代章节的练习
写一个重复字符五次的函数编写测试。
1、 先写测试
在src文件夹中创建一个iteration文件夹,然后在该文件夹目录下创建repeat_test.go文件,编写测试代码如下:
package iteration
import "testing"
func TestRepeat(t *testing.T){
repeated := Repeat("a")
expected := "aaaaa"
if repeated != expected {
t.Errorf("expected '%q' but got '%q'", expected , repeated)
}
}
编写完代码后,会发现Repeat函数未定义。
2、编写Repeat函数
既然Repeat函数未定义,我们便先定义一个Repeat函数,使得测试代码能够跑起来。
在iteration文件夹下添加新的.go文件,命名为repeat.go,代码如下:
package iteration
func Repeat(character string) string{
return ""
}
此时运行go test,会输出expected ‘“aaaaa”’ but got ‘""’,说明我们写的Repeat函数能够成功运行。
接着我们要想办法让Repeat函数能够成功通过测试,观察测试文件可知,只需要把输入的字符重复5遍即可,所以我们可以使用for语句来完成测试。
package iteration
func Repeat(character string) string{
var repeated string
for i:=0 ; i<5 ; i++{
repeated += character
}
return repeated
}
运行go test,便可以发现测试成功通过了。
最后我们尝试编写一个基准测试,具体代码如下:
func BenchmarkRepeat(b *testing.B){
for i:=0 ; i<b.N ; i++{
Repeat("a")
}
}
运行 go test -bench=.来测试代码运行时间,结果如下:
可以看到代码共运行了一千万次,平均每次运行时间为145ns
3、练习
3-1、修改测试代码,以便调用者可以指定字符重复的次数,然后修复代码
要指定字符重复的次数,只需要在Repeat函数中加上一个参数,传入重复次数即可。
修改后的repeat_test.go代码如下:
package iteration
import "testing"
func TestRepeat(t *testing.T){
repeated := Repeat("a", 6)
expected := "aaaaa"
if repeated != expected {
t.Errorf("expected '%q' but got '%q'", expected , repeated)
}
}
接着修改repeat.go中的Repeat函数,使它可以传入两个参数(一个string类型,一个int类型):
package iteration
func Repeat(character string, times int) string{
var repeated string
for i:=0 ; i<times ; i++{
repeated += character
}
return repeated
}
运行go test,会发现代码可以成功跑起来,但是测试出错了,这是因为我设置的重复次数是6,但是结果里面的字符串只有5个a。
将重复次数修改为5,便能通过测试了。
3-2、写一个 ExampleRepeat 来完善你的函数文档
从之前的整数章节中我们可以发现,ExampleRepeat函数是存在与repeat_test.go文件中的。所以我们需要在repeat_test.go文件中新增一个ExampleRepeat函数,同时注意因为这里我们要输出数据,所以要import “fmt”
func ExampleRepeat() {
repeated := Repeat("a", 6)
fmt.Println(repeated)
//Output: aaaaa
}
3-3、看一下 strings 包。找到你认为可能有用的函数,并对它们编写一些测试。
建立一个stringTest文件夹,然后下面相关的strings包的测试内容代码都放在此文件夹中。
(1)查找操作(Contains())
判断给定字符串s中是否包含子串substr, 找到返回true, 找不到返回false,特殊情况下,若字串是空串也返回true。
contains_test.go:
package stringTest
import (
"testing"
"strings"
)
func TestContains(t *testing.T){
contains := strings.Contains("children", "child")
expected := true
if contains != expected {
t.Errorf("expected '%v' but got '%v'", expected, contains)
}
}
这里在错误信息输出处我一开始用的%q,但是报错了,说bool类型的变量不能用%q,所以我改成了%v,用来表示相应类型的默认格式。
最终的输出结果为:
(2)统计操作(Count())
统计字串在原字符串中的出现次数,当字串为空时,返回值为字符串长度+1。
count_test.go:
package stringTest
import(
"testing"
"strings"
)
func TestCount(t *testing.T){
count := strings.Count("hello,world!", "o")
expected := 2
if count != expected{
t.Errorf("expected %q but got %q", expected, count)
}
}
最终的输出结果为:
测试成功。
(3)替换操作(Replace())
用新字符串去替换旧的字符串,共替换次数为n次,最后返回替换后的字符串。特殊的,若n<0会替换掉所有的旧的字符串。
replace_test.go:
package stringTest
import (
"testing"
"strings"
)
func TestReplace(t *testing.T){
ans := strings.Replace("hi hi hi", "i", "ey", 2)
expected := "hey hey hey"
if ans != expected{
t.Errorf("expected %q but got %q", expected, ans)
}
}
最终输出结果为:
测试成功。
二、概念理解
1、TDD
全称为测试驱动开发(Test-Driven Development),其原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。
TDD的基本思路是通过测试来推动整个开发的进行,但是这里的测试驱动开发并不只是单纯的测试工作,而是把需求分析、设计、质量控制量化的过程。
2、重构
重构(Refactoring)是通过调整程序代码,来改善软件的质量、性能,使程序的设计模式和架构更趋于合理,提高软件的扩展性和维护性。
重构可以降低项目的耦合度,使项目更加模块化,有利于项目的开发效率和后期的维护,也有利于代码的再利用。
3、测试
测试时使用人工或者自动的手段来运行或测定某个程序代码的过程,目的在于检验程序是否满足需求,以确保程序能够按照预定的方式正确地运行。
为了发现程序中的错误,在设计测试时我们应尽量设计能够暴露错误的测试用例。
4、基准测试
基准测试是指通过设计科学的测试方法、测试工具和测试系统,实现对一类测试对象的某项性能指标进行定量的和可对比的测试。
基准测试的关键是要获得一致的、可再现的结果,这样可以减少重新运行测试的次数,使得测试的产品和产品产生的数据更加可信。
基准测试的步骤:观察→假设→预测→环境控制→测试→推论和结论
三、程序设计
选择一个算法如“快排”,模仿教程内容结构,写一个Go语言某算法实现TDD的实践报告
1、算法介绍
快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod)。
该方法的基本思想是:
1.先从数列中取出一个数作为基准数。
2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
3.再对左右区间重复第二步,直到各区间只有一个数。
2、代码实现
(1)先写测试文件quicksort_test.go
package quicksort
import "testing"
var arr = [] int {1, 3, 5, 8, 9, 4, 2, 7, 10, 6, 12, 11}
var maxn int = 12
func TestQuicksort(t *testing.T){
Quicksort(arr, 0, 11)
expected := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
flag := true
for i:=0 ; i<12 ; i++{
if arr[i] != expected[i]{
flag = false
}
}
if flag == false{
t.Errorf("Expected %v but got %v", expected, arr)
}
}
(2)然后编写一个简单的quicksort函数使得测试文件能够跑起来。
package quicksort
func Quicksort(arr []int, start int, end int){
return
}
此时运行go test可以得到如下结果:
这时的arr数组并没有进行排序,还是按照原顺序输出。
(3)代码补全,完善quicksort函数。
package quicksort
func Quicksort(arr []int, start int, end int){
if start >= end{
return
}
num := arr[(start+end)/2]
i := start
j := end
for arr[j] > num{
j--
}
for arr[i] < num{
i++
}
temp := arr[i]
arr[i] = arr[j]
arr[j] = temp
i++
j--
if i < end{
Quicksort(arr, i, end)
}
if j > start{
Quicksort(arr, start, j)
}
}
运行go test -v,得到结果如下:
成功通过测试。
(4)基准测试
在测试文件中添加基准测试函数BenchmarkQuicksort(),使测试代码运行b.N次,并测试运行时间。
func BenchmarkQuicksort(b *testing.B){
for i:=0 ; i<b.N ; i++{
var arr = []int {1, 3, 5, 8, 9, 4, 2, 7, 10, 6, 12, 11}
Quicksort(arr, 0, 11)
}
}
运行go test -bench=.命令,测试代码运行的时间,结果如下:
发现运行一次代码要102ns,为了测试这个结果运行了20000000次(两千万次)。