Ginkgo
从官网的介绍了解,Ginkgo是BDD(行为驱动)的测试框架,他最好和Gomega匹配器的库搭配使用,Ginkgo是设计成与匹配器无关。
Bootstrap
生成Suite件
cd path/to/books$ ginkgo bootstrap
生成
package books_testimport (. "github.com/onsi/ginkgo". "github.com/onsi/gomega""testing")func TestBooks(t *testing.T) {RegisterFailHandler(Fail)RunSpecs(t, "Books Suite")}
这会生成一个books_suite_test.go文件,这个是以package的维度生成一个test的入口类
RegisterFailHandler 会注册失败函数,注册给gomega使用
RunSpecs就是运行相应的case
包名:
生成的test文件的包名是books_test,提供了更好的封装性,在test文件中需要手动import原包books来进行使用,这个也是可选的,可以手动修改包名去掉_test后缀
Adding Specs to a Suite
ginkgo generate book
生成一个book_test.go 文件
package books_testimport ("/path/to/books". "github.com/onsi/ginkgo". "github.com/onsi/gomega")var _ = Describe("Book", func() {})
case
var _ = Describe("Book", func() {var (longBook BookshortBook Book)BeforeEach(func() {longBook = Book{Title: "Les Miserables",Author: "Victor Hugo",Pages: 1488,}shortBook = Book{Title: "Fox In Socks",Author: "Dr. Seuss",Pages: 24,}})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"))})})})})
使用 BeforeEach 设置specs的状态,使用 It 来描述一个单一的测试用例
- 在闭包的最顶层定义变量,实现状态,变量共享
- 使用Gomega的
Expect语法来实现比较判断
Fail
直接将case标记为失败
Fail("Failure reason")
在goroutine 中进行测试的时候需要注意使用GinkgoRecover()函数
It("panics in a goroutine", func(done Done) {go func() {defer GinkgoRecover()Ω(doSomething()).Should(BeTrue())close(done)}()})
Logging Output
Ginkgo使用了一个GinkgoWriter 用于输出,GinkgoWriter会聚合input,只有在test失败的时候才会将结果进行输出。当使用ginkgo -v 或者 go test -ginkgo.v 。进行详细模式跑测试的时候,会将结果都输出
Spec语法
It
使用It来描述单一的spec
var _ = Describe("Book", func() {It("can be loaded from JSON", func() {book := NewBookFromJSON(`{"title":"Les Miserables","author":"Victor Hugo","pages":1488}`)Expect(book.Title).To(Equal("Les Miserables"))Expect(book.Author).To(Equal("Victor Hugo"))Expect(book.Pages).To(Equal(1488))})})
Specify
Describe("The foobar service", func() {Context("when calling Foo()", func() {Context("when no ID is provided", func() {Specify("an ErrNoID error is returned", func() {})})})})
BeforeEach
var _ = Describe("Book", func() {var book BookBeforeEach(func() {book = NewBookFromJSON(`{"title":"Les Miserables","author":"Victor Hugo","pages":1488}`)})It("can be loaded from JSON", func() {Expect(book.Title).To(Equal("Les Miserables"))Expect(book.Author).To(Equal("Victor Hugo"))Expect(book.Pages).To(Equal(1488))})It("can extract the author's last name", func() {Expect(book.AuthorLastName()).To(Equal("Hugo"))})})
Describe&Context
var _ = Describe("Book", func() {var (book Bookerr error)BeforeEach(func() {book, err = NewBookFromJSON(`{"title":"Les Miserables","author":"Victor Hugo","pages":1488}`)})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() {book, err = NewBookFromJSON(`{"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"))})})})
在使用多层嵌套的时候,每一个BeforEach是从外到内一次执行
JustBeforeEach
**
在It的函数执行前,在所有的BeforeEach函数执行完成后执行
BeforeSuite AfterSuite
var _ = BeforeSuite(func() {dbRunner = db.NewRunner()err := dbRunner.Start()Expect(err).NotTo(HaveOccurred())dbClient = db.NewClient()err = dbClient.Connect(dbRunner.Address())Expect(err).NotTo(HaveOccurred())})var _ = AfterSuite(func() {dbClient.Cleanup()dbRunner.Stop()})
By
var _ = Describe("Browsing the library", func() {BeforeEach(func() {By("Fetching a token and logging in")authToken, err := authClient.GetToken("gopher", "literati")Exepect(err).NotTo(HaveOccurred())err := libraryClient.Login(authToken)Exepect(err).NotTo(HaveOccurred())})It("should be a pleasant experience", func() {By("Entering an aisle")aisle, err := libraryClient.EnterAisle()Expect(err).NotTo(HaveOccurred())By("Browsing for books")books, err := aisle.GetBooks()Expect(err).NotTo(HaveOccurred())Expect(books).To(HaveLen(7))By("Finding a particular book")book, err := books.FindByTitle("Les Miserables")Expect(err).NotTo(HaveOccurred())Expect(book.Title).To(Equal("Les Miserables"))By("Check the book out")err := libraryClient.CheckOut(book)Expect(err).NotTo(HaveOccurred())books, err := aisle.GetBooks()Expect(books).To(HaveLen(6))Expect(books).NotTo(ContainElement(book))})})
By 会通过GinkgoWriter输出内容,在case成功的情况下,不会输出,失败时会输出。By 还可以接收一个 函数,这个函数会被立即执行
跳过测试
编译期跳过测试,Ps: PDescribe 和 XDescribe 函数的效果是一样的
PDescribe("some behavior", func() { ... })PContext("some scenario", func() { ... })PIt("some assertion")PMeasure("some measurement")XDescribe("some behavior", func() { ... })XContext("some scenario", func() { ... })XIt("some assertion")XMeasure("some measurement")
运行时通过Skip函数跳过测试
It("should do something, if it can", func() {if !someCondition {Skip("special condition wasn't met")}// assertions go here})
Focused Specs
声明后只会跑这些case了
FDescribe("some behavior", func() { ... })FContext("some scenario", func() { ... })FIt("some assertion", func() { ... })
Gomega
和Ginkgo结合,只需要注册相应的失败函数即可,通过bootstrap创建的suite类会直接注册好
gomega.RegisterFailHandler(ginkgo.Fail)
有两种语法
Ω(ACTUAL).Should(Equal(EXPECTED))Ω(ACTUAL).ShouldNot(Equal(EXPECTED))
Expect(ACTUAL).To(Equal(EXPECTED))Expect(ACTUAL).NotTo(Equal(EXPECTED))Expect(ACTUAL).ToNot(Equal(EXPECTED))
单独使用gomega
func TestFarmHasCow(t *testing.T) {g := NewGomegaWithT(t)f := farm.New([]string{"Cow", "Horse"})g.Expect(f.HasCow()).To(BeTrue(), "Farm should have cow")}
常见用法
测试panic
panic测试的函数需要是一个没有返回值的函数
Expect(func() {conf.Image.GetStringFromConf(emptyMap, true)}).Should(Panic())
文档参考
http://onsi.github.io/ginkgo/
http://onsi.github.io/gomega/
TestMain的使用
https://medium.com/goingogo/why-use-testmain-for-testing-in-go-dafb52b406bc
