社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
操作系统:Ubuntu18.04.5LTS-amd64
编辑器:VScode
TDD过程
TDD功能
不改变系统的外部功能,只对内部的结构进行重新的整理。通过重构,不断的调整系统的设计模式和架构,改善其质量、性能,提高其扩展性和维护性,使系统对于需求的变更始终具有较强的适应能力。
TDD与重构有着紧密的联系,在TDD过程中可以看到,代码正是通过不断重构来适应测试,进而满足用户需求的。
单元测试是功能测试,测试各函数的功能是否正常,其还包括覆盖率测试,可视化、量化展示测试的覆盖率(如哪些函数在测试中未涉及等),帮助程序员尽可能测试所有相关的代码。
Go的单元测试框架的要求:
基准测试是测试代码性能的方法,主要通过测试CPU和内存等因素,来评估代码性能,帮助程序员提高代码性能。
Go的基准测试框架的要求:
原测试代码部分:
package iteration
import "testing"
func TestRepeat(t *testing.T) {
repeated := Repeat("a")
expected := "aaaaa"
if repeated != expected {
t.Errorf("repeated '%q' expected '%q'", repeated, expected)
}
}
func BenchmarkRepeat(b *testing.B) {
for i := 0; i < b.N; i++ {
Repeat("a")
}
}
原代码
package iteration
func Repeat(ch string) string {
var repeated string
for i := 0; i < 5; i++ {
repeated += ch
}
return repeated
}
package iteration
import "testing"
func TestRepeat(t *testing.T) {
repeated := Repeat("a", 5)
expected := "aaaaa"
if repeated != expected {
t.Errorf("repeated '%q' expected '%q'", repeated, expected)
}
}
func BenchmarkRepeat(b *testing.B) {
for i := 0; i < b.N; i++ {
Repeat("a", 5)
}
}
$ go test //测试出错
# github.com/LEEzanhui/iteration
./iteration_test.go:6:20: too many arguments in call to Repeat
have (string, number)
want (string)
FAIL github.com/LEEzanhui/iteration [build failed]
package iteration
func Repeat(ch string, times int) string {
var repeated string
for i := 0; i < times; i++ {
repeated += ch
}
return repeated
}
$ go test //单元测试成功
PASS
ok github.com/LEEzanhui/iteration 0.001s
$ go test -bench=. //基准测试
goos: linux
goarch: amd64
pkg: github.com/LEEzanhui/iteration
BenchmarkRepeat-8 10000000 126 ns/op
PASS
ok github.com/LEEzanhui/iteration 1.402s
测试成功,代码完成重构;
通过查阅文档可知这是一个示例函数,其有格式要求:以Example
开头,如果示例函数包含以 “Output” 开头的行注释,在运行测试时,go 会将示例函数的输出和 “Output” 注释中的值做比较;
在iteration_test.go
文件中,编写一个ExampleRepeat
函数,演示对Repeat
函数的调用:
func ExampleRepeat() {
str1 := "a"
str2 := Repeat(str1, 5)
fmt.Println(str2)
// Output: aaaaa
}
可以运行该函数:
如果运行结果与注释中的Output不同,会报错:
访问https://godoc.org/strings可以查看这个包的详细文档,深入了解各个函数。
选择了Count、Index和ToLower三个函数;测试了这些函数的普通情况和一些特殊情况,如空串等,具体可见代码;
package teststringpkg
import (
"strings"
"testing"
)
func TestCount(t *testing.T) {
assertCorrectMessage := func(t *testing.T, got, want int) {
t.Helper()
if got != want {
t.Errorf("got '%d' want '%d'", got, want)
}
}
t.Run("count number", func(t *testing.T) {
got := strings.Count("engineer", "e")
want := 3
assertCorrectMessage(t, got, want)
})
t.Run("substr is empty", func(t *testing.T) {
got := strings.Count("engineer", "")
want := 9
assertCorrectMessage(t, got, want)
})
}
func TestIndex(t *testing.T) {
assertCorrectMessage := func(t *testing.T, got, want int) {
t.Helper()
if got != want {
t.Errorf("got '%d' want '%d'", got, want)
}
}
t.Run("find index", func(t *testing.T) {
got := strings.Index("chicken", "ck")
want := 3
assertCorrectMessage(t, got, want)
})
t.Run("substr not present", func(t *testing.T) {
got := strings.Index("chicken", "chicks")
want := -1
assertCorrectMessage(t, got, want)
})
}
func TestToLower(t *testing.T) {
assertCorrectMessage := func(t *testing.T, got, want string) {
t.Helper()
if got != want {
t.Errorf("got '%q' want '%q'", got, want)
}
}
t.Run("with symbol", func(t *testing.T) {
got := strings.ToLower("chicken_!?")
want := "chicken_!?"
assertCorrectMessage(t, got, want)
})
t.Run("with upper case", func(t *testing.T) {
got := strings.ToLower("GOPher")
want := "gopher"
assertCorrectMessage(t, got, want)
})
}
运行指令:
$ go test github.com/LEEzanhui/teststringpkg -run ^函数名
如:
$ go test github.com/LEEzanhui/teststringpkg -run ^TestCount
运行结果均为:
ok github.com/LEEzanhui/teststringpkg 0.001s
如下图,是测试Index函数:
快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
ArrayCompare
函数来完成比较,并对该函数也写了测试函数;package quicksort
import "testing"
func TestQuickSort(t *testing.T) {
cases := []struct {
in, want []int
}{
{[]int{4, 2, 7, 10, 6, 1, 3}, []int{1, 2, 3, 4, 6, 7, 10}},
{[]int{7, 8, 5, 4, 1, 0, 2, 9, 3, 6}, []int{0, 1, 1, 3, 4, 5, 6, 7, 8, 9}},
}
for _, c := range cases {
got := QuickSort(c.in, len(c.in))
if !ArrayCompare(got, c.want) {
t.Errorf("Quicksort(%d) == %d, want %d", c.in, got, c.want)
}
}
}
func TestArrayCompare(t *testing.T) {
cases := []struct {
in1, in2 []int
want bool
}{
{[]int{4, 2, 7, 10, 6, 1, 3}, []int{1, 2, 3, 4, 6, 7, 10}, false},
{[]int{1, 2, 3}, []int{1, 2, 3}, true},
{[]int{1, 2, 3}, []int{1, 2}, false},
}
for _, c := range cases {
got := ArrayCompare(c.in1, c.in2)
if got != c.want {
t.Errorf("ArrayCompare(%d, %d) == %t, want %t", c.in1, c.in2, got, c.want)
}
}
}
显然测试函数无法运行,因为QuickSort和ArrayCompare都未定义;
package quicksort
// QuickSort quicksort
func QuickSort(in []int, length int) []int {
out := make([]int, length)
return out
}
// ArrayCompare : retun true if two int array is same
func ArrayCompare(arr1 []int, arr2 []int) bool {
return true
}
显然无法通过测试:
package quicksort
// QuickSort quicksort
func QuickSort(in []int, length int) []int {
out := make([]int, length)
for i := 0; i < length; i++ {
out[i] = in[i]
}
QSRecur(&out, 0, len(out)-1)
return out
}
// QSRecur recursive called by QuickSort
func QSRecur(in *[]int, l, r int) {
if l < r {
i, j, baseNum := l, r, (*in)[l]
for i < j {
for i < j && (*in)[j] >= baseNum {
j--
}
if i < j {
(*in)[i] = (*in)[j]
i++
}
for i < j && (*in)[i] < baseNum {
i++
}
if i < j {
(*in)[j] = (*in)[i]
j--
}
}
(*in)[i] = baseNum
QSRecur(in, l, i-1)
QSRecur(in, i+1, r)
}
}
// ArrayCompare : retun true if two int array is same
func ArrayCompare(arr1 []int, arr2 []int) bool {
if len(arr1) != len(arr2) {
return false
}
for i := 0; i < len(arr1); i++ {
if arr1[i] != arr2[i] {
return false
}
}
return true
}
测试
除直接用指令启动测试外,vscode也提供了按键实现一键测试,一般在测试文件的开头或函数上方;
选择文件开头的run package tests
,会执行该文件下的所有测试函数,并附带部分参数的结果,见下图:
可见其不但测试了结果,而且还考虑了运行时间timeout并显示了测试覆盖率,上图显示了测试覆盖了100%的语句;
修改quicksort_test.go,加入基准测试
func BenchmarkQuickSort(b *testing.B) {
for i := 0; i < b.N; i++ {
arr := []int{7, 8, 5, 4, 1, 0, 2, 9, 3, 6}
QuickSort(arr, len(arr))
}
}
测试指令
$ go test -bench=.
结果:
本次实验的主要目标是了解TDD、测试等概念,并进行实践,对于Go语言这门测试驱动开发的语言,该技能是基础;此外还进一步了解了Go的语法知识,如数组和切片等,能够在编程中进行运用。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!