Ginkgo测试框架入门

为什么选择Ginkgo

目前基于 go 语言较为流行的测试框架:

Ginkgo (BDD)

在需求沟通上,若开发对需求的理解有偏差,就容易发生摩托变单车的事情。而BDD行为驱动测试就是为了解决这个问题,即先写功能描述。开发,测试,产品等都可以看懂,能低成本地纠正错误。

BDD可以得到我们对产品的理解提供反馈,更符合互联网团队目前对功能点的验收测试,也可结合TDD框架testify做更为细致的测试。

使用过rspec框架的会对Ginkgo的语法比较亲切,可以减少学习成本,Ginkgo的函数表达能力比较强, 功能也比较齐全

Name Ginkgo GoConvey testify
Assertions Gomega
Style spec spec assert
Equal
IsSame
DeepEqual
True
False
Nil
Empty
Error
Implements
IsType
StringContains
StringMatches
Collection
Panics
HasLen
Matches
Satisfy
Within

框架简介

Ginkgo是golang的BDD类型的测试框架, ginkgo官方推进的匹配库是 gomega , 可以用它代替go内置的Testing的语法库,可以减少代码书写量,且语意更明确。

Quick Start

Step1. 安装

$ go get github.com/onsi/ginkgo/ginkgo
$ go get github.com/onsi/gomega/...

Step2. 生成测试套件Suite

$ cd path/to/books
$ ginkgo bootstrap

上述命令会自动生成,如下代码:

//path/to/books/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")
}

需要注意的是这里package为 books_test , go中package的名字需要跟文件所在目录名一致packageName, 而packageName_test则是特例,在go中是允许的

Step3. 生成specs

$ ginkgo generate book

上述命令会自动生成,如下代码:

//path/to/books/book_test.go
package books_test

import (
    . "/path/to/books"
    . "github.com/onsi/ginkgo"
    . "github.com/onsi/gomega"
)

var _ = Describe("Book", func() {

})

Step4. 添加specs

//path/to/books/books_suite_test.go

var _ = Describe("Book", func() {
    var (
        book Book
        err error
        json string
    )

    BeforeEach(func() {
        json = `{
            "title":"Les Miserables",
            "author":"Victor Hugo",
            "pages":1488
        }`
    })

    JustBeforeEach(func() {
        book, err = NewBookFromJSON(json)
    })

    Describe("loading from JSON", func() {
        Context("when the JSON parses succesfully", func() {
            It("should populate the fields correctly", func() {
                Expect(book.Title).To(Equal("Les Miserables"))
                Expect(book.Author).To(Equal("Victor Hugo"))
                Expect(book.Pages).To(Equal(1488))
            })

            It("should not error", func() {
                Expect(err).NotTo(HaveOccurred())
            })
        })

        Context("when the JSON fails to parse", func() {
            BeforeEach(func() {
                json = `{
                    "title":"Les Miserables",
                    "author":"Victor Hugo",
                    "pages":1488oops
                }`
            })

            It("should return the zero-value for the book", func() {
                Expect(book).To(BeZero())
            })

            It("should error", func() {
                Expect(err).To(HaveOccurred())
            })
        })
    })

    Describe("Extracting the author's last name", func() {
        It("should correctly identify and return the last name", func() {
            Expect(book.AuthorLastName()).To(Equal("Hugo"))
        })
    })
})

method

Ginkgo提供了一系列拥可以嵌套的函数,来更丰富地描述测试,下面举几个比较常用的函数

Describe

描述一种行为或者一个方法

Context

丰富Describe所描述的行为或方法,增加条件语句,尽可能全地覆盖各种condition

it

申明用例, 期望这个用例得到的结果

Expect

这个是gomega提供的方法,用来断言结果

BeforeEach

会在每个小于等于beforeEach嵌套层的it函数之前运行,来设置公用的数据变量

JustBeforeEach

会在所有BeforeEach执行之后运行,在每个小于等于JustBeforeEach嵌套层的it函数之前运行, 可以有效避免重复创建

Mock

Ginkgo没有提供相关的mock方法,Ginkgo作者认为可以通过依赖注入和通过interface来实现, 他认为这比mock和stubs更清楚和富有表现力,话虽如此,但 Gomock 这个package,Ginkgo作者是比较推荐的,他实现mock相对来说比较简单

import (
    "code.google.com/p/gomock/gomock"

    . github.com/onsi/ginkgo
    . github.com/onsi/gomega
)

var _ = Describe("Consumer", func() {
    var (
        mockCtrl *gomock.Controller
        mockThing *mockthing.MockThing
        consumer *Consumer
    )

    BeforeEach(func() {
        mockCtrl = gomock.NewController(GinkgoT())
        mockThing = mockthing.NewMockThing(mockCtrl)
        consumer = NewConsumer(mockThing)
    })

    AfterEach(func() {
        mockCtrl.Finish()
    })

    It("should consume things", func() {
        mockThing.EXPECT().OmNom()
        consumer.Consume()
    })
})

补充

此文档仅供快速入门,更多详细用法可以查询 官方文档