正在写测试用例

This commit is contained in:
astaxie
2012-11-08 18:25:31 +08:00
parent ac5f171a10
commit 01bbfc8390
2 changed files with 57 additions and 25 deletions

24
11.1.md
View File

@@ -1,16 +1,16 @@
# 11.1 错误处理
Go语言设计的时候主要的特点简洁、明白简洁是指语法和C类似相当的简单明白是指任何语句都是很明显的不含有任何隐的东西,Go在设计错误的时候也是一样。我们知道C语言里面返回错误是使用-1或者nil之类的返回信息表示错误,但是对于使用者来说这个返回值根本不知道什么意思而Go里面当发生异常时返回一个Error类型通过前面编写的代码我们发现在Go语言里面有很多地方都使用了Error类型很多函数都有这个Error返回。例如`os.Open`函数打开文件失败时返回一个不为nil的error
Go语言主要的设计准则简洁、明白简洁是指语法和C类似相当的简单明白是指任何语句都是很明显的不含有任何隐的东西,在错误处理方案的设计中也贯彻了这一思想。我们知道C语言里面是通过返回-1或者NULL之类的信息表示错误,但是对于使用者来说不查看相应的API说明文档根本搞不清楚这个返回值究竟代表什么意思比如:返回0是成功还是失败,而Go定义了一个叫做error类型,来显式表达错误。在使用时通过把返回的error变量与nil的比较来判定操作是否成功。例如`os.Open`函数打开文件失败时返回一个不为nil的error变量
func Open(name string) (file *File, err error)
下面这个例子通过`os.Open`打开一个文件,如果出错那么会执行`log.Fatal`打印出来错误信息:
下面这个例子通过调用`os.Open`打开一个文件,如果出现错误,那么就会调用`log.Fatal`输出错误信息:
f, err := os.Open("filename.ext")
if err != nil {
log.Fatal(err)
}
其实这样的error返回在Go语言的很多内置包里面有很多我们这个小节将详细的介绍这些error是怎么设计,以及在我们设计的Web应用如何更好的处理error。
类似于`os.Open`函数标准包中所有可能出错的API都返回了一个error变量以方便错误处理这个小节将详细的介绍error类型的设计以及在设计的Web应用如何更好的处理error。
## Error类型
error类型是一个接口类型这是它的定义
@@ -28,7 +28,7 @@ error是一个内置的类型变量我们可以在/builtin/包下面找到相
func (e *errorString) Error() string {
return e.s
}
你可以通过`errors.New`把一个字符串转化为errorString然后返回error接口,其内部实现如下:
你可以通过`errors.New`把一个字符串转化为errorString以得到一个满足接口error的对象,其内部实现如下:
// New returns an error that formats as the given text.
func New(text string) error {
@@ -44,7 +44,7 @@ error是一个内置的类型变量我们可以在/builtin/包下面找到相
// implementation
}
我们在调用Sqrt的时候传递一个负数然后返回一个non-nil的值,那么我们就可以根据判断调用fmt.Println打印出来错误,fmt包在处理error时会调用Error方法打印出来错误信息,请看下面调用的示例代码:
在下面的例子中,我们在调用Sqrt的时候传递一个负数,然后就得到了non-nil的error对象将此对象与nil比较结果为true所以fmt.Println(fmt包在处理error时会调用Error方法)被调用,以输出错误,请看下面调用的示例代码:
f, err := Sqrt(-1)
if err != nil {
@@ -52,7 +52,7 @@ error是一个内置的类型变量我们可以在/builtin/包下面找到相
}
## 自定义Error
通过上面的介绍我们知道error是一个interface定义,那么在一些我们自定义的包里面我们可以实现这接口定义,从而实现自己的错误定义,请看下面Json包中定义的自定义错误这个例子
通过上面的介绍我们知道error是一个interface,所以在实现自己的包的时候,通过定义实现此接口的结构,我们可以实现自己的错误定义,请看来自Json包的示例
type SyntaxError struct {
msg string // 错误描述
@@ -71,7 +71,7 @@ offset字段在调用Error的时候不会被打印但是我们可以通过类
return err
}
上面这个例子简单的演示了如何自定义Error处理。error接口只有实现Error方法就可以实现但是如果我们还有其他需要实现的方法呢我们可以参考一下net包下面的错误定义
上面例子简单的演示了如何自定义Error类型。但是如果我们还需要更复杂的错误处理呢?此时,我们来参考一下net包采用的方法
package net
@@ -81,7 +81,7 @@ offset字段在调用Error的时候不会被打印但是我们可以通过类
Temporary() bool // Is the error temporary?
}
这样我们在调用的地方可以通过类型断言判断是否是net.Error,然后根据错误的类型判断如何处理,例如下面的例子,如果一个网络发生临时性错误,那么可以sleep秒之后重试:
在调用的地方通过类型断言err是不是net.Error,来细化错误的处理,例如下面的例子,如果一个网络发生临时性错误,那么将会sleep 1秒之后重试:
if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
time.Sleep(1e9)
@@ -92,7 +92,7 @@ offset字段在调用Error的时候不会被打印但是我们可以通过类
}
## 错误处理
Go语言里面错误处理是非常重要的Go语言设计鼓励大家在发生错误时进行检测,而不是其他语言那样抛出异常,虽然每个错误处理都写的话会显得代码很长,但是我们通过检测函数可以复用这些错误处理逻辑
Go在错误处理上采用了与C类似的检查返回值的方式,而不是其他多数主流语言采用的异常方式,这造成了代码编写上的一个很大的缺点:错误处理代码的冗余,对于这种情况是我们通过复用检测函数来减少类似的代码
请看下面这个例子代码:
@@ -113,7 +113,7 @@ Go语言里面错误处理是非常重要的Go语言设计鼓励大家在发
}
}
上面的例子中获取数据和模板展示调用时都有检测错误,当有错误发生时,调用了统一的处理函数`http.Error`返回给客户端500错误码并显示相应的错误数据。但是当越来越多的HandleFunc加入之后这样的错误处理逻辑代码就会越来越多其实我们可以通过自定义路由器来缩减代码。
上面的例子中获取数据和模板展示调用时都有检测错误,当有错误发生时,调用了统一的处理函数`http.Error`返回给客户端500错误码并显示相应的错误数据。但是当越来越多的HandleFunc加入之后这样的错误处理逻辑代码就会越来越多其实我们可以通过自定义路由器来缩减代码(实现的思路可以参考第三章的HTTP详解)
type appHandler func(http.ResponseWriter, *http.Request) error
@@ -176,10 +176,10 @@ Go语言里面错误处理是非常重要的Go语言设计鼓励大家在发
return nil
}
如上所示,这样在我们访问view的时候可以根据不同的情况获取不同的错误码和错误信息虽然这个和第一个版本的代码量差不多但是这个现实的错误更加明显,提示的错误信息更加友好,扩展性也比第一个更好。
如上所示在我们访问view的时候可以根据不同的情况获取不同的错误码和错误信息虽然这个和第一个版本的代码量差不多但是这个显示的错误更加明显,提示的错误信息更加友好,扩展性也比第一个更好。
## 总结
错误处理在Go语言是相当重要error虽然只是一个接口但是他的变化可以很多,我们可以根据自己的需求来实现不同程度的处理,而且自定义错误处理也是相当的方便,最后介绍的错误处理机制也是给大家提供了一个如何友好的提示错误,并且如何做到更好的扩展性的一种设计,对于我们后期的运维,错误定位都起到了相当好的作用
在程序设计中容错是相当重要的一部分工作在Go中它是通过错误处理来实现error虽然只是一个接口但是变化可以很多,我们可以根据自己的需求来实现不同的处理,最后介绍的错误处理方案希望能给大家在如何设计更好Web错误处理方案上带来一点思路
## links
* [目录](<preface.md>)

58
11.3.md
View File

@@ -1,14 +1,46 @@
# 11.3 Go怎么写测试用例
## 如何编写测试用例
func TestXXX(t *testing.T) { ... }
## 如何编写压力测试
func BenchmarkXXX(b *testing.B) { ... }
## links
* [目录](<preface.md>)
* 上一节: [使用GDB调试](<11.2.md>)
# 11.3 Go怎么写测试用例
开发程序其中很重要的一点是测试我们如何保证代码的质量如何保证每个函数是可运行运行结果是正确的又如何保证写出来的代码性能是好的我们知道单元测试的重点在于发现程序设计或实现的逻辑错误使问题及早暴露便于问题的定位解决而性能测试的重点在于发现程序设计上的一些问题让线上的程序能够在高并发的情况下还能保持稳定。本小节将带着这一连串的问题来讲解Go语言中如何来实现单元测试和性能测试。
Go语言中自带有一个轻量级的测试框架`testing`和自带的`go test`命令来实现单元测试和性能测试,`testing`框架和其他语言中的测试框架类似,你可以基于这个框架写针对相应函数的测试用例,也可以基于该框架写相应的压力测试用例,那么接下来让我们一一来看一下怎么写。
## 如何编写测试用例
由于`go test`命令只能在一个相应的目录下执行所有文件,所以我们接下来新建一个项目目录`gotest`,这样我们所有的代码和测试代码都在这个目录下。
接下来我们再改目录下面创建两个文件gotest.go和gotest_test.go
1. gotest.go:这个文件里面我们是创建了一个包,里面有一个函数实现了除法运算:
package gotest
import (
"errors"
)
func intdiv(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("除数不能为0")
}
return a / b, nil
}
2. gotest_test.go:这是我们的单元测试文件,但是记住下面的这些原则:
- 文件名必须是`_test.go`结尾的,这样在执行`go test`的时候才会执行到相应的代码
- 包名必须和被测试文件的包名一致例如上面的包名是gotest那么test文件的包名也必须是gotest
- 你必须import `testing`这个包
- 所有的测试用例函数必须是`Test`开头
- 测试用例会按照源代码中写的顺序依次执行
- 测试函数`TestXxx()`的参数是`testing.T`,我们可以使用该类型来记录错误或者是测试状态
- 测试格式:`func TestXxx (t *testing.T)`,`Xxx`部分可以为任意的字符串组合,但是首字母不能是小写字母[a-z],例如`Testintdiv`是错误的函数名。
- `testing.T`
## 如何编写压力测试
func BenchmarkXXX(b *testing.B) { ... }
## links
* [目录](<preface.md>)
* 上一节: [使用GDB调试](<11.2.md>)
* 下一节: [小结](<11.4.md>)