Ginkgo&Gomega
前言
最近公司项目实现标准化,打算给项目加上单元测试和集成测试,在Go的项目中由调研后打算采用Ginkgo
和Gomega
,看了下官方文档和github上的测试后写下点心得
Ginkgo
是Go语言的一个行为驱动开发(BDD)风格的测试框架,通常和Gomega一起使用
Gomega
是一个匹配/断言库,通常与Ginkgo测试框架搭配使用
下面的文章对上面的文档进行了参考和引用
对于测试的方法和使用请参考具体的文档,本文章主要介绍实战的使用
安装
安装Ginkgo
1 |
|
安装Gomega
1 |
|
开始
Ginkgo
与Go自带测试挂钩,允许使用go test
运行Ginkgo
套件
创建套件
如果要为一个包编写Ginkgo
测试,首先需要使用命令创建套件
1 |
|
上诉命令会生成文件:
1 |
|
创建完成后就可以在当前包下使用ginkgo
或者go test
执行测试套件
添加Spec
上面的空测试套件没有什么价值,我们需要在此套接下编写测试(Spec)。虽然可以在books_suite_test.go
中编写测试,但是推荐分离到独立的文件中,特别是包中有多个需要被测试的源文件的情况下
1 |
|
1 |
|
Describe
块用来组织Specs
,其中可以包含任意数量的It
:可以在Describe
、Context
这两种容器块内编写Spec
,每个Spec
写在It
块中;为了贴合自然语言,可以使用It
的别名Specify
- 使用
Describe
和Context
来标识组织代码的具体行为 BeforeEach
:在Spec
(It块)运行之前执行,嵌套Describe
时最外层BeforeEach
先执行AfterEach
:在Spec
运行之后执行,嵌套Describe
时最内层AfterEach
先执行JustBeforeEach
:在It
块,所有BeforeEach
之后执行,在It
之前运行
实战
现在要对网页后端的多个API接口进行集成测试
初步
1 |
|
- 上诉的思想就是每个测试组件都提前定义好
request
和response
,通过发送给client
的request
加上Gomega
断言收到的response来进行测试 - 把每个测试组件都要使用的变脸,如
url、method、client
等放最初的全局变量 - 每次使用前都要重新初始化
client
、request
、response
则放入BeforeEach
(如果使用了response.Body
)可以使用AfterEach
进行关闭
对上面不同的Gomega断言类型进行说明
gomega.Expect(res.StatusCode).Should(gomega.Equal(http.StatusOK))
Should
和To
相同,表示肯定,gomega.Equal
则是表示断言相等,连起来就是我们实际得到的res.StatusCode
应该和http.StatusOK
相同,相同测试成功,不同测试失败gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
ShouldNot
和NotTo
相同,表示否定,gomega.HaveOccurred()
指示non-nill
的error
则成功,连起来就是如果出现non-nil
的error
就测试失败,error
为nil
则测试成功,则是非常典型的Go错误测试模式gomega.Expect(res).To(test.RepresentJSONObject(http.StatusOK))
自定义匹配规则,只要实现
Match
、FailureMessage
、NegatedFailureMessage
方法的接口就行,好处就是匹配规则灵活多变(传入的http.StatusOK作为expected去Match Expect中的 res),匹配结果返回的是false
和non-err
则为测试失败
1 |
|
改进
1 |
|
1 |
|
采用自定义封装匹配规则和
gomega.HaveOccurred()
错误断言对变量定义和初始化进行封装,因为每次在调用后都要重新初始化
TCli
,所以采用创建套件时初始化TCli
和每次It
结束后初始化的方法1
2
3
4//在ginkgo bootstrap创建套件的文件中,表示启动测试套件的每一个总Describe运行前都要执行的代码
var _ = BeforeSuite(func() {
warpper.InitTCli()
})对
BeforeEach
中client
发送request
和接收response
的代码进行封装1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16func (t *TeClient) Do(method, url, reqJson string) error {
adr := "http://" + ip + url
t.Req, t.Err = http.NewRequest(method, adr, strings.NewReader(reqJson))
t.Req.Header.Add("xxx", "xxx")
t.Req.Header.Add("xxx", "xxx")
if reqJson != "" {
t.Req.Header.Add("Content-Type", "application/json")
}
t.Res, t.Err = t.Client.Do(t.Req)
if t.Err != nil {
err := t.Err
return err
}
return nil
}把测试组件相同的代码抽出成函数调用的方式,这样测试组件中的代码简介明了
Header.Add(“xxx”,"xxx")
一般都是token
、安全验证,因为在测试中使用的身份是一样的对于有传如
request.Body
的POST
方法才加Header.Add("Content-Type", "application/json")
,GET
中不需要传入request.Body
就不需要了
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!