- TDD(Test-Driven development) 测试驱动开发
- 内置的 testing 库 、 表格驱动、样本测试、TestMain
- 第三方:goconvey
- Monkey 猴子补丁
- 数据库 mock
- travisCI
- 代码覆盖率
TDD
- 快速实现功能
- 再设计和重构
软件测试
在指定的条件下,操做程序,发现程序错误git
单元测试
对软件的组成单元进行测试,最小单位:函数github
包含三个步骤:golang
- 指定输入
- 指定预期
- 函数结果和指定的预期比较
指标:web
- 代码覆盖率:运行测试执行的代码占总代码的行数
testing 库的使用
// Hello ... func Hello() string { return "Hello World" } 复制代码
// 传统测试 func TestHello(t *testing.T) { result := Hello() want := "Hello World" if result == want { t.Logf("Hello() = %v, want %v", result, want) } else { t.Errorf("Hello() = %v, want %v", result, want) } want2 := "Hello world" if result == want2 { t.Logf("Hello() = %v, want %v", result, want) } else { t.Errorf("Hello() = %v, want %v", result, want) } } // 表格驱动测试: 使用匿名结构体,逻辑更清晰 func TestHelloWithTable(t *testing.T) { tests := []struct { name string want string }{ // TODO: Add test cases. { name: "test for hello", want: "Hello World", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := Hello(); got != tt.want { t.Errorf("Hello() = %v, want %v", got, tt.want) } }) } } 复制代码
运行:sql
// mode one go test // equal to : go test . 执行当前目录下的测试文件 // mode two go test ./.. // 加上路径参数,能够执行指定目录下的测试文件 复制代码
样本测试:数据库
func ExampleHello() { fmt.Println(Hello()) // Output: // Hello World } 复制代码
TestMain:bash
包的测试运行以前执行函数
func TestMain(m *testing.M) { fmt.Println("Before ====================") code := m.Run() fmt.Println("End ====================") os.Exit(code) } 复制代码
testing 包含下面几种方法:单元测试
- Log | Logf
- Error | ErrorF
- Fatal | FatalF
备注:
X_
覆盖率:
go test -cover go test -coverprofile=cover.out go tool cover -html=cover.out -o coverage.html 复制代码
第三方:goconvey
- 支持断言
- 支持嵌套
- 彻底兼容内置 testing
- 提供 web UI
func TestAdd_Two(t *testing.T) { Convey("test add", t, func() { Convey("0 + 0", func() { So(Add(0, 0), ShouldEqual, 0) }) Convey("-1 + 0", func() { So(Add(-1, 0), ShouldEqual, -1) }) }) } func TestFloatToString_Two(t *testing.T) { Convey("test float to string", t, func() { Convey("1.0/3.0", func() { result := FloatToString(1.0, 3.0) So(result, ShouldContainSubstring, "%") So(len(result), ShouldEqual, 6) So(result, ShouldEqual, "33.33%") }) }) } 复制代码
goconvey // 启动 web 界面 复制代码
Monkey 猴子补丁
- 函数打桩
- 过程打桩
- 方法打桩
// 函数 func main() { monkey.Patch(fmt.Println, func(a ...interface{}) (n int, err error) { s := make([]interface{}, len(a)) for i, v := range a { s[i] = strings.Replace(fmt.Sprint(v), "hell", "*bleep*", -1) } return fmt.Fprintln(os.Stdout, s...) }) fmt.Println("what the hell?") // what the *bleep*? } 复制代码
// 方法 func main() { var d *net.Dialer // Has to be a pointer to because `Dial` has a pointer receiver monkey.PatchInstanceMethod(reflect.TypeOf(d), "Dial", func(_ *net.Dialer, _, _ string) (net.Conn, error) { return nil, fmt.Errorf("no dialing allowed") }) _, err := http.Get("http://google.com") fmt.Println(err) // Get http://google.com: no dialing allowed } 复制代码
// 过程 guard := Patch(DestroyResource, func(_ string) { }) defer guard.Unpatch() 复制代码
使用思路,被测函数中须要使用的其余依赖函数,进行打桩处理。
sqlmock
对 sql 的执行过程进行打桩。
- 建立模拟链接
- 编写 原生 sql 语句
- 编写 返回值 或者 错误信息
- 判断执行结果和预设的返回值