Let's Go
TDD
What is TDD?
TDD是测试驱动开发(Test-Driven Development)的英文简称,是敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。TDD虽是敏捷方法的核心实践,但不只适用于XP(Extreme Programming),同样可以适用于其他开发方法和过程。
Why TDD?
- “测试先行”可以提高产品质量,开发人员一边写测试用例,一边编写业务代码来推动项目
- 可以提前了解清楚需求,因为写测试用例的前提是弄懂了需求
- 有着快速的反馈,有丰富的测试用例来覆盖业务代码,一旦代码出错,就可以及时发现问题并改正
TDD设计周期
- 写一个新的测试用例
- 运行新加的测试用例,看到它失败(还没写功能代码)
- 编写业务代码,对开发代码做很小的修改,目的就是让新加的测试通过
- 运行所有的测试用例,然后看到所有测试都通过了
- 移掉重复的代码,对代码进行重构
初识TDD——迭代
与别的编程语言一样,循环和迭代是不可或缺的功能。在 Go 中 for 用来循环和迭代,与别的语言不同之处在于Go 语言没有 while,do,until 这几个关键字,你只能使用 for。
先写测试
首先我们先为我们实现的迭代循环功能编写测试代码
package iteration
import "testing"
func TestRepeat(t *testing.T) {
repeated := Repeat("a")
expected := "aaaaaaa"
if repeated != expected {
t.Errorf("expected '%q' but got '%q'", expected, repeated)
}
}
使用最少的代码来让失败的测试先跑起来
接下来我们完成循环迭代功能代码
package iteration
func Repeat(character string) string {
return "a"
}
并运行测试程序进行测试
go test repeat_test.go
得到了如下结果:
完善代码,使得它能够通过测试
通过测试回馈信息得知我们的代码功能不正确,需要完善代码
func Repeat(character string) string {
var repeated string
for i := 0; i < 7; i++ {
repeated = repeated + character
}
return repeated
}
再次运行测试程序,得到正确的结果
重构
重构(Refactoring)就是通过调整程序代码改善软件的质量、性能,使其程序的设计模式和架构更趋合理,提高软件的扩展性和维护性。
为了使repeat程序更加简洁易懂,更加具有维护性,我们在保证功能的前提下重构了代码
const repeatCount = 5
func Repeat(character string) string {
var repeated string
for i := 0; i < repeatCount; i++ {
repeated += character
}
return repeated
}
基准测试
基准测试是指通过设计科学的测试方法、测试工具和测试系统,实现对一类测试对象的某项性能指标进行定量的和可对比的测试。
在现实生活中,实现功能远远不能满足人们的需求。良好的性能也是人们判断该程序是否便利的重要标准。所以,我们需要通过基准测试来测试程序性能。
repeat基准测试
接下来进行repeat的基准测试,基准测试代码如下:
func BenchmarkRepeat(b *testing.B) {
for i := 0; i < b.N; i++ {
Repeat("a")
}
}
运行基准测试
go test -bench .
得到的基准测试结果如下:
字符拼接基准测试比较
为了更好的对比相同功能不同实现方法的性能比较,我们分别对两种不同的字符拼接程序进行了基准测试
func StrPlus1(a []string) string {
var s, sep string
for i := 0; i < len(a); i++ {
s += sep + a[i]
sep = " "
}
return s
}
func StrPlus2(a []string) string {
return strings.Join(a, " ")
}
编写基准测试比较StrPlus1和StrPlus2两种不同字符拼接方法的性能
func BenchmarkStrPlus1(b *testing.B) {
for i := 0; i < b.N; i++ {
StrPlus1([]string{"xxx", "bbb", "aaa"})
}
}
func BenchmarkStrPlus2(b *testing.B) {
for i := 0; i < b.N; i++ {
StrPlus2([]string{"xxx", "bbb", "aaa"})
}
}
进行基准测试比较字符拼接算法的效率
go test -bench .
得到的结果如下:
从结果我们可以分析出StrPlus2的字符拼接效率要高与StrPlus1
TDD实践——快速排序算法
快速排序算法
快速排序算法的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
先写测试
首先我们先为我们实现的快速排序算法编写测试代码
func TestQuickSort(t *testing.T) {
arr := []int{2, 4, 8, 5, 3, 6, 1, 7, 9, 0}
res := QuickSort(arr, 0, 7)
expected := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
for i := 0; i < 8; i++ {
if res[i] != expected[i] {
t.Errorf("\n result %v\n but expect %v\n", res, expect)
break
}
}
}
使用最少的代码来让失败的测试先跑起来
接下来简单完成代码让测试可以跑起来
func QuickSort(list []int, left int, right int) []int {
return list
}
并运行测试程序进行测试
go test quicksort_test.go
得到的结果如下:
完善代码,使得它能够通过测试
接下来完成快速排序算法功能
func partition(nums []int, left int, right int) int {
value := nums[left]
for left < right {
for nums[right] >= value && left < right {
right--
}
nums[left] = nums[right]
for nums[left] < value && left < right {
left++
}
nums[right] = nums[left]
}
nums[left] = value
return left
}
func QuickSort(list []int, left int, right int) []int {
if left < right {
middle := partition(list, left, right)
QuickSort(list, left, middle-1)
QuickSort(list, middle+1, right)
}
return list
}
再运行测试得到的正确结果如下:
基准测试
接下来通过基准测试测试快速排序的性能,完成基准测试代码如下:
func BenchmarkQuickSort(b *testing.B) {
arr := []int{2, 4, 5, 8, 6, 3, 1, 7}
for i := 0; i < b.N; i++ {
QuickSort(arr, 0, 7)
}
}
运行基准测试
go test -bench .
得到的基准测试结果如下: