什么是ginkgo:
ginkgo是一个用go写的BDD(Behavior Driven Development)的测试框架,一般用于Go服务的集成测试。
ginkgo的特点
BDD的代码风格
Describe("delete app api",func(){
It("should delete app permanently",func(){
...}) It("should delete app failed if services existed", func(){
...}) )
讯享网
这段ginkgo的心得体会摘自七牛云测试团队,大卡尔的使用体会
讯享网Ginkgo定义的DSL语法(Describe/Context/It)可以非常方便的帮助大家组织和编排测试用例。 在BDD模式中,测试用例的标题书写,要非常注意表达,要能清晰的指明用例测试的业务场景。 只有这样才能极大的增强用例的可读性,降低使用和维护的心智负担。 可读性这一点,在自动化测试用例设计原则上,非常重要。因为测试用例不同于一般意义上的程序, 它在绝大部分场景下,看起来都像是一段段独立的方法,每个方法背后隐藏的业务逻辑也是细小的,不具通识性。 这个问题在用例量少的情况下,还不明显。但当用例数量上到一定量级,你会发现,如果能快速理解用例到底是能做什么的,真的非常重要。 而这正是BDD能补足的地方。 不过还是要强调,Ginkgo只是提供对BDD模式的支持,你的用例最终呈现的效果,还是依赖你自己的书写。
ginkgo安装
$ go get github.com/onsi/ginkgo/ginkgo $ go get github.com/onsi/gomega/...
ginkgo常用模块
讯享网常用的10个:It、Context、Describe、BeforeEach、AfterEach、JustBeforeEach、BeforeSuite、AfterSuite、By、Fail
It模块
It是测试例的基本单位,即It包含的代码就算一个测试用例 It可以理解维测试代码的最小单元
Context和Describe 模块
讯享网Context和Describe的功能都是将一个或多个测试例归类 一个describe可以包含多个context,一个context可以包含多个IT模块
BeforeEach模块
BeforeEach是每个测试例执行前执行该段代码 比如创建数据库连接就可以使用BeforeEach ,每个BeforeEach只在当前域内起作用。 执行顺序是同一层级的顺序执行,不同层级的从外层到里层以此执行。类似与 全局变量和局部变量的区别
AfterEach模块
讯享网AfterEach是每个测试例执行后执行该段代码 比如销毁数据库连接,一般用于测试例执行完成后进行数据清理,也可以用于结果判断
JustBeforeEach,
JustBeforeEach是在BeforeEach执行之后,测试例执行之前执行 测试用例执行前的一些前置操作
BeforeSuite模块
讯享网BeforeSuite是在该测试集执行前执行,即该文件夹内的测试例执行之前 BeforeSuite和AfterSuite写在_suite_test.go文件中,会在所有测试例执行之前和之后执行 如果BeforeSuite执行失败,则这个测试集都不会被执行
Tip:使用C中断执行时,AfterSuite仍然会被执行,需要再使用一次C中断
AfterSuite模块
AfterSuite是在该测试集执行后执行,即该文件夹内的测试例执行完后
By模块
讯享网By是打印信息,内容只能是字符串,只会在测试例失败后打印,一般用于调试和定位问题
Fail模块
Fail是标志该测试例运行结果为失败,并打印里面的信息
Specify模块
讯享网Specify和It功能完全一样,It属于其简写
ginkgo的三个标志
F、X和P,可以用在Describe、Context、It等任何包含测试例的模块
P的含义是Pending,即不执行,用法和F一样,规则的外层的生效
It("should do something, if it can", func() {
if !someCondition {
Skip("special condition wasn't met") } // assertions go here })
ginkgo 并发设置
讯享网ginkgo -p 使用默认并发数 ginkgo -nodes=N 自己设定并发数
默认并发数是用的参数runtime.NumCPU()值,即逻辑CPU个数,大于4时,用runtime.NumCPU()-1
如果需要显示实时日志,需要添加 -stream参数
并发执行时打印的日志是汇总后经过合并处理再打印的,所以看起来比较规范, 每个测试例的内容也都打印在一起,但时不实时,如果需要实时打印,加-stream参数,缺点是每个测试例日志交叉打印
ginkgo goroutine设置
在平时的代码中,我们经常会看到需要做异步处理的测试用例。但是这块的逻辑如果处理不好,用例可能会因为死锁或者未设置超时时间而异常卡住,非常的恼人。好在Ginkgo专门提供了原生的异步支持,能大大降低此类问题的风险。类似用法:
讯享网 It("should post to the channel, eventually", func(done Done) {
c := make(chan string, 0) go DoSomething(c) Expect(<-c).To(ContainSubstring("Done!")) close(done) }, 0.2)
0.2的单位是秒
ginkgo性能测试
使用这个模块Measure
Measure("it should do something hard efficiently", func(b Benchmarker) {
runtime := b.Time("runtime", func() {
output := SomethingHard() Expect(output).To(Equal(17)) }) Ω(runtime.Seconds()).Should(BeNumerically("<", 0.2), "SomethingHard() shouldn't take too long.") b.RecordValue("disk usage (in MB)", HowMuchDiskSpaceDidYouUse()) }, 10)
ginkgo命令行使用
讯享网ginkgo bootstrap <FLAGS> 创建测试关联文件 ginkgo generate <filename(s)> 生成测试代码模版 ginkgo nodot 更新测试套件中的nodot声明 ginkgo convert /path/to/package 转变包文件格式为ginkgo可识别的格式 ginkgo unfocus (or ginkgo blur) 递归地取消当前目录下所有集中测试的焦点 ginkgo version 输出版本信息
ginkgo初级实战
1.本地gopath/src下创建一个项目books
2. 创建 books.go 文件
package books import ( "encoding/json" "fmt" ) type Book struct {
Title string Author string Pages int } func (b Book) AuthorLastName() (interface{
}, interface{
}) {
fmt.Println("AuthorLastName") return b.Author,b.Title } func (b Book) CategoryByLength() string{
if b.Pages >300{
return "NOVEL" } else if b.Pages<100 {
return "SMALL STORY" } else {
return "SHORT STORY" } } func NewBookFromJSON(json json.Decoder) error {
book:=json.Decode(json) return book }
- 如果go版本高于1.11 引入go moudle的原因 , 创建go.mod包管理文件 go mod init
当前文件夹下会生成两个文件 go.mod go.sum
讯享网->cat go.mod module books go 1.14 require ( github.com/onsi/ginkgo v1.12.0 github.com/onsi/gomega v1.9.0 golang.org/x/sync v0.0.0-054-43a5402ce75a // indirect ) ->cat go.sum github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= golang.org/x/net v0.0.0-101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= golang.org/x/net v0.0.0-101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/sync v0.0.0-146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= golang.org/x/sync v0.0.0-054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-948-bdbb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA= golang.org/x/sys v0.0.0-948-bdbb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/xerrors v0.0.0-122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-325-20d25e h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-325-20d25e/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-613-ddf1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-613-ddf1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= ->
- 创建测试关联文件
进入项目当前路径下执行 ginkgo bootstrap 项目下会创建一个 books_suite_test.go文件,为测试用例入口
package books_test import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "testing" ) func TestBooks(t *testing.T) {
RegisterFailHandler(Fail) RunSpecs(t, "Books Suite") }
- 创建测试模版文件 ginkgo generate books 当前的包名字叫 books所以 创建出来的测试模版文件叫
books_test.go
讯享网package books_test import ( . "/path/to/books" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Book", func() {
})
package books_test import ( . "books" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Book", func() {
var ( longBook Book shortBook Book smallBook Book ) BeforeEach(func() {
longBook = Book{
Title: "Les Miserables", Author: "Victor Hugo", Pages: 1488, } shortBook = Book{
Title: "Fox In Socks", Author: "Dr. Seuss", Pages: 240, } smallBook = Book{
Title: "go program", Author: "caochunhui", Pages: 20, } }) Describe("Categorizing book length", func() {
Context("With more than 300 pages", func() {
It("should be a novel", func() {
Expect(longBook.CategoryByLength()).To(Equal("NOVEL")) }) }) Context("With fewer than 300 pages", func() {
It("should be a short story", func() {
Expect(shortBook.CategoryByLength()).To(Equal("SHORT STORY")) }) }) Context("With fewer than 100 pages", func() {
It("should be a small story", func() {
Expect(smallBook.CategoryByLength()).To(Equal("SMALL STORY")) }) }) }) })
当前这个测试代码中我们写了三个用例,分别测试books源代码中CategoryByLength功能所对应的三个逻辑,即根据书页的多少判断书本的类型,测试代码只需要覆盖所有的逻辑即可。
6. 执行测试代码
这块有三种执行办法
go test -v
ginkgo
还可以 ginko build 编译成二进制,执行二进制

可以看到输出了测试结果
7.输出JUnit测试报告
ginkgo支持输出junit的测试报告 ,这块需要修改一下套件文件books_suite_test.go 将原始的
RunSpecs(t, “Books Suite”) 直接运行,修改为生成report报告运行方式
讯享网func TestBooks(t *testing.T) {
RegisterFailHandler(Fail) //RunSpecs(t, "Books Suite") junitReporter := reporters.NewJUnitReporter("junit.xml") RunSpecsWithDefaultAndCustomReporters(t, "Books Suite", []Reporter{
junitReporter}) }
<?xml version="1.0" encoding="UTF-8"?> <testsuite name="Books Suite" tests="3" failures="0" errors="0" time="0"> <testcase name="Book Categorizing book length With more than 300 pages should be a novel" classname="Books Suite" time="5.0482e-05"></testcase> <testcase name="Book Categorizing book length With fewer than 300 pages should be a short story" classname="Books Suite" time="1.111e-06"></testcase> <testcase name="Book Categorizing book length With fewer than 100 pages should be a small story" classname="Books Suite" time="7.7e-07"></testcase> </testsuite>
一个简单的ginkgo测试框架体验到此结束,后续更新高级玩法。
参考文档
http://onsi.github.io/ginkgo/
https://s0godoc0org.icopy.site/github.com/onsi/ginkgo
https://blog.csdn.net/goodboynihaohao/article/details/

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/18218.html