diff --git a/11.3.md b/11.3.md index 920c90a7..639437b7 100644 --- a/11.3.md +++ b/11.3.md @@ -16,7 +16,7 @@ Go语言中自带有一个轻量级的测试框架`testing`和自带的`go test` "errors" ) - func intdiv(a, b float64) (float64, error) { + func Division(a, b float64) (float64, error) { if b == 0 { return 0, errors.New("除数不能为0") } @@ -32,13 +32,117 @@ Go语言中自带有一个轻量级的测试框架`testing`和自带的`go test` - 所有的测试用例函数必须是`Test`开头 - 测试用例会按照源代码中写的顺序依次执行 - 测试函数`TestXxx()`的参数是`testing.T`,我们可以使用该类型来记录错误或者是测试状态 - - 测试格式:`func TestXxx (t *testing.T)`,`Xxx`部分可以为任意的字符串组合,但是首字母不能是小写字母[a-z],例如`Testintdiv`是错误的函数名。 - - `testing.T` + - 测试格式:`func TestXxx (t *testing.T)`,`Xxx`部分可以为任意的字母数字的组合,但是首字母不能是小写字母[a-z],例如`Testintdiv`是错误的函数名。 + - 函数中通过调用`testing.T`的`Error`, `Errorf`, `FailNow`, `Fatal`, `FatalIf`方法,说明测试不通过,调用`Log`方法用来记录测试的信息。 + + 下面是我们的测试用例的代码: + + package gotest + + import ( + "testing" + ) + + func Test_Division_1(t *testing.T) { + if i, e := Division(6, 2); i != 3 || e != nil { //try a unit test on function + t.Error("除法函数测试没通过") // 如果不是如预期的那么就报错 + } else { + t.Log("第一个测试通过了") //记录一些你期望记录的信息 + } + } + + func Test_Division_2(t *testing.T) { + t.Error("就是不通过") + } + + 我们在项目目录下面执行`go test`,就会显示如下信息: + + --- FAIL: Test_Division_2 (0.00 seconds) + gotest_test.go:16: 就是不通过 + FAIL + exit status 1 + FAIL gotest 0.013s + 从这个结果显示测试没有通过,因为在第二个测试函数中我们写死了测试不通过的代码`t.Error`,那么我们的第一个函数执行的情况怎么样呢?默认情况下执行`go test`是不会显示测试通过的信息的,我们需要带上参数`go test -v`,这样就会显示如下信息: + + === RUN Test_Division_1 + --- PASS: Test_Division_1 (0.00 seconds) + gotest_test.go:11: 第一个测试通过了 + === RUN Test_Division_2 + --- FAIL: Test_Division_2 (0.00 seconds) + gotest_test.go:16: 就是不通过 + FAIL + exit status 1 + FAIL gotest 0.012s + 上面的输出详细的展示了这个测试的过程,我们看到测试函数1`Test_Division_1`测试通过,而测试函数2`Test_Division_2`测试失败了,最后得出结论测试不通过。接下来我们把测试函数2修改成如下代码: + + func Test_Division_2(t *testing.T) { + if _, e := Division(6, 0); e == nil { //try a unit test on function + t.Error("Division did not work as expected.") // 如果不是如预期的那么就报错 + } else { + t.Log("one test passed.", e) //记录一些你期望记录的信息 + } + } + 然后我们执行`go test -v`,就显示如下信息,测试通过了: + + === RUN Test_Division_1 + --- PASS: Test_Division_1 (0.00 seconds) + gotest_test.go:11: 第一个测试通过了 + === RUN Test_Division_2 + --- PASS: Test_Division_2 (0.00 seconds) + gotest_test.go:20: one test passed. 除数不能为0 + PASS + ok gotest 0.013s +## 如何编写压力测试 +压力测试是用来检测我们写的函数是否有足够好的性能,和写单元功能测试类似,需要注意以下几点: + +- 压力测试函数必须如下格式,其中`XXX`可以是任意字母数字的组合,但是首字母不能是小写字母 -## 如何编写压力测试 + func BenchmarkXXX(b *testing.B) { ... } + +- `go test`不会默认执行压力测试的函数,如果要执行压力测试需要带上参数`-test.bench`,语法:`-test.bench="test_name_regex"`,例如`go test -test.bench=".*"`表示测试全部的压力测试函数 +- 压力测试函数中记得在循环中使用`testing.B.N`从而使得压力测试可以正常的运行 +- 文件名也必须以`_test.go`结尾 + +下面我们新建一个压力测试文件webbench_test.go,代码如下所示: + + package gotest + + import ( + "testing" + ) + + func Benchmark_Division(b *testing.B) { + for i := 0; i < b.N; i++ { //use b.N for looping + Division(4, 5) + } + } + + func Benchmark_TimeConsumingFunction(b *testing.B) { + b.StopTimer() //调用该函数停止压力测试的时间计数 + + //做一些初始化的工作,例如读取文件数据,数据库连接之类的, + //这样这些时间不影响我们测试函数本身的性能 + + b.StartTimer() //重新开始时间 + for i := 0; i < b.N; i++ { + Division(4, 5) + } + } + + +我们执行命令`go test -file webbench_test.go -test.bench=".*"`,可以看到如下结果: + + PASS + Benchmark_Division 500000000 7.76 ns/op + Benchmark_TimeConsumingFunction 500000000 7.80 ns/op + ok gotest 9.364s + +上面的结果显示我们没有执行任何`TestXXX`的单元测试函数,显示的结果只执行了压力测试函数,第一条显示了`Benchmark_Division`执行了500000000次,每次的执行平均时间是7.76纳秒,第二条显示了`Benchmark_TimeConsumingFunction`执行了500000000,每次的平均执行时间是7.80纳秒。最后一条显示总过的执行时间。 + +## 小结 +通过上面对单元测试和压力测试的编写,我们知道Go语言里面的`testing`包虽然轻量,但是编写测试用例和压力测试非常简单,而且使用内置的命令`go test`就可以非常方便的执行这些单元测试和压力测试,这样我们每次修改完函数执行一下单元测试和压力测试,可以简单的做回归测试。 - func BenchmarkXXX(b *testing.B) { ... } ## links * [目录]() diff --git a/11.4.md b/11.4.md index 8ab0f766..4e4cca28 100644 --- a/11.4.md +++ b/11.4.md @@ -1,4 +1,5 @@ # 11.4 小结 +本章我们通过三个小节分别介绍了Go语言中如何处理错误,如何设计错误处理,然后第二小节介绍了如何通过GDB来调试程序,通过GDB我们可以单步调试、可以查看变量、修改变量、打印执行过程等,最后我们介绍了如何利用Go语言自带的轻量级框架`testing`来编写单元测试和压力测试,使用`go test`就可以方便的执行这些测试,使得我们将来代码升级修改之后很方便的进行回归测试。这一章也许对于你编写程序逻辑没有任何帮助,但是对于你编写出来的程序代码保持高质量是至关重要的,因为一个好的Web应用必定有良好的错误处理机制(错误提示的友好、可扩展性)、有好的单元测试和压力测试以保证上线之后代码能够保持良好的性能和按预期的运行。 ## links * [目录]() diff --git a/12.1.md b/12.1.md new file mode 100644 index 00000000..9786c643 --- /dev/null +++ b/12.1.md @@ -0,0 +1,6 @@ +# 12.1 应用日志 + +## links + * [目录]() + * 上一章: [部署与维护](<12.md>) + * 下一节: [网站错误处理](<12.2.md>) \ No newline at end of file diff --git a/12.2.md b/12.2.md new file mode 100644 index 00000000..683cd3ae --- /dev/null +++ b/12.2.md @@ -0,0 +1,6 @@ +# 12.2 网站错误处理 + +## links + * [目录]() + * 上一章: [应用日志](<12.1.md>) + * 下一节: [应用部署](<12.3.md>) \ No newline at end of file diff --git a/12.3.md b/12.3.md new file mode 100644 index 00000000..b587e7eb --- /dev/null +++ b/12.3.md @@ -0,0 +1,6 @@ +# 12.3 应用部署 + +## links + * [目录]() + * 上一章: [网站错误处理](<12.2.md>) + * 下一节: [备份和恢复](<12.4.md>) \ No newline at end of file diff --git a/12.4.md b/12.4.md new file mode 100644 index 00000000..406a3920 --- /dev/null +++ b/12.4.md @@ -0,0 +1,6 @@ +# 12.4 备份和恢复 + +## links + * [目录]() + * 上一章: [应用部署](<12.3.md>) + * 下一节: [小结](<12.5.md>) \ No newline at end of file diff --git a/12.5.md b/12.5.md new file mode 100644 index 00000000..2b3296b8 --- /dev/null +++ b/12.5.md @@ -0,0 +1,6 @@ +# 12.5 小结 + +## links + * [目录]() + * 上一章: [备份和恢复](<12.4.md>) + * 下一节: [构建博客系统](<13.md>) \ No newline at end of file diff --git a/12.md b/12.md new file mode 100644 index 00000000..a59312e0 --- /dev/null +++ b/12.md @@ -0,0 +1,13 @@ +# 12 部署与维护 + +## 目录 + * 1 [应用日志](12.1.md) + * 2 [网站错误处理](12.2.md) + * 3 [应用部署](12.3.md) + * 4 [备份和恢复](12.4.md) + * 5 [小结](12.5.md) + +## links + * [目录]() + * 上一章: [第十一章总结](<11.4.md>) + * 下一节: [应用日志](<12.1.md>) \ No newline at end of file diff --git a/preface.md b/preface.md index 33d52ee9..d2523971 100644 --- a/preface.md +++ b/preface.md @@ -70,12 +70,12 @@ - 11.2 [使用GDB调试](11.2.md) - 11.3 [Go怎么写测试用例](11.3.md) - 11.4 [小结](11.4.md) -* 12.部署与维护 - - 12.1 应用日志 - - 12.2 网站错误处理 - - 12.3 应用部署 - - 12.4 备份和恢复 - - 12.5 小结 +* 12.[部署与维护](12.md) + - 12.1 [应用日志](12.1.md) + - 12.2 [网站错误处理](12.2.md) + - 12.3 [应用部署](12.3.md) + - 12.4 [备份和恢复](12.4.md) + - 12.5 [小结](12.5.md) * 13.构建博客系统  - 13.1 创建数据库表  - 13.2 建立对象类