迭代——算法实现

《Learn Go with tests》
完成该教程“迭代”章节的练习,理解TDD、重构、测试、基准测试等概念。自己选择一个算法如“快排”,模仿教程内容结构,写一个Go语言某算法实现TDD实践报告

写测试

  • 创建包目录github.com/user/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)
    }
}
  • 在包目录下运行go test
    由于尚未创建包含Repeat函数的文件,测试结果为失败
    在这里插入图片描述

补充代码使能通过测试

  • 尝试让失败的测试跑起来,在包目录下创建repeat.go,代码如下
package iteration

func Repeat(character string) string {
    return ""
}
  • 再次在包目录下运行go test
    在这里插入图片描述
  • 把代码补充完整,使这个函数具有重复字符 5 次的功能
func Repeat(character string) string {
    var repeated string
    for i := 0; i < 5; i++ {
        repeated = repeated + character
    }
    return repeated
}
  • 再次在包目录下运行go test
    在这里插入图片描述
    成功通过测试

测试
Go拥有一个轻量级的测试框架,它由 go test 命令和 testing 包构成。
你可以通过创建一个名字以 _test.go 结尾的,包含名为 TestXXX 且签名为 func (t *testing.T) 函数的文件来编写测试。 测试框架会运行每一个这样的函数;若该函数调用了像 t.Error 或 t.Fail 这样表示失败的函数,此测试即表示失败。

重构

我理解为使用更加简化、高效、可重用的代码达到相同的功能。
例如if...else,for ... range改动,将可复用代码封装成函数,抽象出新的接口层等

下面为对repeat.go的重构,引入另一个构造(construct)+= 赋值运算符。

const repeatCount = 5

func Repeat(character string) string {
    var repeated string
    for i := 0; i < repeatCount; i++ {
        repeated += character
    }
    return repeated
}

基准测试

在Go语言中,提供了测试函数性能(CPU和Memory)的测试方法,基准测试。
基准测试主要用来测试CPU和内存的效率问题,来评估被测代码的性能。测试人员可以根据这些性能指标的反馈,来优化我们的代码,进而提高性能。
使用规则如下:
1.基准测试的代码文件必须以_test.go结尾。
2.基准测试的函数必须以Benchmark开头。
3.基准测试函数必须接受一个指向testing.B类型的指针作为唯一参数。
4.测试代码需要写在for循环中,并且循环中的最大之是b.N。
5.用go test -bench="."来运行基准测试(windows下)

  • 在包目录下创建bench_test.go,代码如下
package iteration

import "testing"

func BenchmarkRepeat(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Repeat("a")
    }
}
  • 在包目录下运行go test -bench="."
    在这里插入图片描述
    以上结果说明运行一次这个函数需要 128纳秒(在我的电脑上)。这挺不错的,为了测试它运行了 9173259 次。

TDD

TDD,也就是测试驱动开发(Test-Driven development),是一种“测试先行”的程序设计方法论,其基本流程围绕着测试->编码(重构)->测试的循环展开。

TDD三原则

  1. 除非为了通过一个单元测试,否则不允许编写任何产品代码。
  2. 在一个单元测试中只允许编写刚好能够导致失败的内容。
  3. 一次只能写通过一项单元测试的产品代码,不能多写。

根据三原则,TDD的开发过程描述如图:
在这里插入图片描述

练习

  • 修改测试代码,以便调用者可以指定字符重复的次数,然后修复代码
  1. 首先修改测试代码

repeat_test.go

package iteration

import "testing"

func TestRepeat(t *testing.T) {
    repeated := Repeat("a",5)
    expected := "aaaaa"

    if repeated != expected {
        t.Errorf("expected '%q' but got '%q'", expected, repeated)
    }
}
  1. 在包目录下运行go test
    在这里插入图片描述
  2. 根据出错信息,相应地修改业务代码,使之通过测试
package iteration

func Repeat(character string,len int) string {
    var repeated string
    for i := 0; i < len; i++ {
        repeated += character
    }
    return repeated
}

在这里插入图片描述

  • 写一个 ExampleRepeat 来完善你的函数文档
    写一个ExampleRepeat函数来展现Repeat函数的实际功能
    示例将出现在 godoc 的文档中,这将使代码更容易理解
    repeat_test.go中添加函数
func ExampleRepeat() {
	repeated := Repeat("a", 8)
	fmt.Println(repeated)
	// Output: aaaaaaaa
}

运行go test -v,结果如下
在这里插入图片描述
查看go document中Example的具体效果

  1. 安装godoc
$env:GOPROXY = "https://goproxy.io"
go get -v  golang.org/x/tools/cmd/godoc
  1. 启动Go Documentation Server
    godoc -http=:6060
  2. 在浏览器中,打开url
    http://localhost:6060
  3. godoc命令会去Go语言根目录和环境变量GOPATH包含的工作区目录中查找代码包。我们可以通过加入标记-goroot来制定一个Go语言根目录
    godoc -goroot E:\Workplace\go
  4. 刷新浏览器
    在这里插入图片描述
    在这里插入图片描述

示例测试

  1. 函数名为func Example{要提供示例的函数名}
  2. 检测单行输出格式为“// Output: <期望字符串>”;
  3. 检测多行输出格式为“// Output: \ <期望字符串> \ <期望字符串>”,每个期望字符串占一行;
  4. 检测无序输出格式为"// Unordered output: \ <期望字符串> \ <期望字符串>",每个期望字符串占一行;
  5. 测试字符串时会自动忽略字符串前后的空白字符;
  6. 如果测试函数中没有“Output”标识,则该测试函数不会被执行;
  7. 执行测试可以使用go test,此时该目录下的其他测试文件也会一并执行;
  8. 执行测试可以使用go test <xxx_test.go>,此时仅执行特定文件中的测试函数;
  • 看一下 strings 包。找到你认为可能有用的函数,并对它们编写一些测试。投入时间学习标准库会慢慢得到回报。
    在这里插入图片描述
    godoc:strings

选择函数:Count,Index
在这里插入图片描述
在这里插入图片描述

package main

import (
	"fmt"
	"strings"
)

func main() {
	//Count
	fmt.Println(strings.Count("apple", "p"))
	fmt.Println(strings.Count("five", "")) // before & after each rune
	//Index
	fmt.Println(strings.Index("chicken", "ken"))
	fmt.Println(strings.Index("chicken", "abc"))
}

在这里插入图片描述

TDD实践报告:选择排序

回顾TDD开发过程

在这里插入图片描述

写测试

  • 创建包目录github.com/user/selectsort
  • 在包目录下创建SelectSort_test.go,代码如下
package selectsort

import (
	"testing"
)

func TestSelectSort(t *testing.T) {
	var flag bool
	flag = true
	arr := []int{6, 9, 2, 5, 3, 1, 4, 7, 0, 8}
	sorted := SelectSort(arr)
	expected := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

	for i := 0; i < 10; i++ {
		if sorted[i] != expected[i] {
			flag = false
		}
	}
	if !flag {
		t.Errorf("expected %v but got %v", expected, sorted)
	}
}


测试失败

在这里插入图片描述

写产品代码

用最少的代码先使测试跑起来

package selectsort

func SelectSort(data []int) []int{
	return data
}

根据出错信息补充代码

package selectsort

func SelectSort(data []int) []int {
	length := len(data)
	for i := 0; i < length; i++ {
		temp := data[i]
		flag := i
		for j := i + 1; j < length; j++ {
			if data[j] < temp {
				temp = data[j]
				flag = j
			}
		}
		if flag != i {
			data[flag] = data[i]
			data[i] = temp
		}
	}
	return data
}

测试通过

在这里插入图片描述

重构

用下标索引flag指向数组中最小值即可,删除temp

package selectsort

func SelectSort(data []int) []int {
	length := len(data)
	for i := 0; i < length; i++ {
		flag := i
		for j := i + 1; j < length; j++ {
			if data[j] < data[flag] {
				flag = j
			}
		}
		if flag != i {
			var temp int
			temp = data[flag]
			data[flag] = data[i]
			data[i] = temp
		}
	}
	return data
}

小结

go test

单元测试——测试和验证代码的框架

对软件中的最小可测试单元进行检查和验证

  • 准备一个 go 源码文件,在命名文件时需要让文件必须以_test结尾。
  • 在命令行使用go test命令,自动测试源码包下的所有test文件

这里介绍几个常用的参数:
-bench regexp 执行相应的 benchmarks,例如 -bench=.;
-cover 开启测试覆盖率;
-run regexp 只运行 regexp 匹配的函数,例如 -run=Array 那么就执行包含有 Array 开头的函数;
-v 显示测试的详细命令。

  • 每个测试用例函数需要以Test为前缀,例如:
    func TestXXX( t *testing.T )
  • 在 go test 后跟文件名,表示测试这个文件里的所有测试用例。
    go test helloworld_test.go
  • go test指定文件时默认执行文件内的所有测试用例。可以使用-run参数选择需要的测试用例单独执行,例如
    go test -v -run TestA select_test.go

-单元测试框架提供的日志方法

Log 打印日志,同时结束测试
Logf 格式化打印日志,同时结束测试
Error 打印错误日志,同时结束测试
Errorf 格式化打印错误日志,同时结束测试
Fatal 打印致命日志,同时结束测试
Fatalf 格式化打印致命日志,同时结束测试

基准测试——获得代码内存占用和运行效率的性能数据

在Go语言中,提供了测试函数性能(CPU和Memory)的测试方法,基准测试。
基准测试主要用来测试CPU和内存的效率问题,来评估被测代码的性能。测试人员可以根据这些性能指标的反馈,来优化我们的代码,进而提高性能。

使用规则如下:
1.基准测试的代码文件必须以_test.go结尾。
2.基准测试的函数必须以Benchmark开头。
3.基准测试函数必须接受一个指向testing.B类型的指针作为唯一参数。
4.测试代码需要写在for循环中,并且循环中的最大之是b.N。
5.用go test -bench="."来运行基准测试(windows下)

例子
go test -v -bench="." -benchtime=5s benchmark_test.go

  • bench="."表示运行 benchmark_test.go 文件里的所有基准测试,和单元测试中的-run类似。
    bench=“Alloc” 指定只测试 Benchmark_Alloc() 函数。
  • benchtime 通过此参数可以自定义测试时间
  • 在命令行中添加-benchmem参数以显示内存分配情况

基准测试中的计时器控制

    // 重置计时器
    b.ResetTimer()
    // 停止计时器
    b.StopTimer()
    // 开始计时器
    b.StartTimer()
    ```
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值