编写你的第一个测试
Ginkgo与Go现有的测试基础设施挂钩。这允许您使用 `go test` 运行Ginkgo套件。
这也意味着Ginkgo测试可以与传统的Go测试一起使用。 go test和ginkgo都可以在你的套件中运行所有测试。
启动一个套件
要为包编写Ginkgo测试,您必须先初始化(bootstrap
)Ginkgo测试套件。假设您有一个名为 books
的包:
$ cd path/to/books
$ ginkgo bootstrap
例如:
$ cat path/to/books/books.go
package books
type Book struct {
Title string
Author string
Pages int
}
func (b *Book) CategoryByLength() string {
if b.Pages >= 300 {
return "NOVEL"
}
return "SHORT STORY"
}
这将生成一个名为 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")
}
让我们分析一下:
Go允许我们在
books
包同目录下指定books_test
包。使用books_test
而不是books
可以让我们不破坏books
包的封装:你的测试需要导入books
包并从外部访问它,就像导入其它任何包一样。这是进入包,测试其内部结构并进行更多行为测试的首选。当然,您可以选择不使用此功能 - 只需把package books_test
改为package books
即可。我们通过
.
将ginkgo
和gomega
包导入了测试的顶级命名空间。如果您不想这样做,可以参考下面的Avoiding Dot Imports 部分。TestBooks
是一个testing
测试。当您运行go test
或ginkgo命令时
,Go测试运行器将运行此功能。RegisterFailHandler(Fail)
: Ginkgo 测试通过调用Fail(description string)
功能来表示失败。我们使用RegisterFailHandler
将此函数传递给Gomega。这是Ginkgo和Gomega之间的唯一连接点。RunSpecs(t *testing.T, suiteDescription string)通知
Ginkgo启动测试套件。如果您的任何specs
失败,Ginkgo将自动使testing.T
失败。
此时您可以运行您的套件:
$ ginkgo #or go test
=== RUN TestBootstrap
Running Suite: Books Suite
==========================
Random Seed: 1378936983
Will run 0 of 0 specs
Ran 0 of 0 Specs in 0.000 seconds
SUCCESS! -- 0 Passed | 0 Failed | 0 Pending | 0 Skipped
--- PASS: TestBootstrap (0.00 seconds)
PASS
ok books 0.019s
给套件添加Specs
一个空的测试套件不是很有趣。虽然您可以开始直接将测试添加到books_suite_test.go
中,但您可能更愿意将测试分成单独的文件(特别是对于包含多个文件的包)。让我们为book.go
模型添加一个测试文件:
$ ginkgo generate book
这将生成一个名为book_test.go
的文件,其中包含:
package books_test
import (
. "/path/to/books"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Book", func() {
})
让我们拆解分析一下:
- 我们将
ginkgo
和gomega
包导入顶级命名空间。虽然非常方便,但严格来说这并不是必需的。如果您想避免这种情况,请查看下面的Avoiding Dot Imports部分。 类似地,我们导入
books
包,因为我们使用特殊的books_test
包来将我们的测试与我们的代码隔离开来。方便起见,我们将
books
包导入命名空间。您可以通过编辑生成的测试文件来选择不使用这些选项。
我们使用Ginkgo的
Describe(text string, body func()) bool
函数添加了顶级Describe
容器。var _ = ...
技巧允许我们在顶级评估Describe
,而不必将其包装在func init() {}
Describe 中的功能将包含我们的Specs
。现在让我们添加一些来测试从JSON中加载books
:
var _ = Describe("Book", func() {
var (
longBook Book
shortBook 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"))
})
})
})
})
让我们分别分析一下:
Ginkgo广泛使用闭包(⚠️闭包不是私有,闭的意思不是“封闭内部状态”,而是“封闭外部状态”!),允许您构建描述性测试套件。
您应该使用
Describe
和Context
容器来表达性地组织代码的行为。您可以使用
BeforeEach
为您的Specs
设置状态。您可以使用It
来指定单个Spec
。为了在
BeforeEach
和It
之间共享状态,您使用闭包变量,通常在最相关的Describe
或Context
容器的顶部定义。我们使用Gomega的
Expect
语法来对CategoryByLength()
方法产生期望值。
假设具有此行为的Book
模型,运行测试将产生:
$ ginkgo # or go test
=== RUN TestBootstrap
Running Suite: Books Suite
==========================
Random Seed: 1378938274
Will run 2 of 2 specs
••
Ran 2 of 2 Specs in 0.000 seconds
SUCCESS! -- 2 Passed | 0 Failed | 0 Pending | 0 Skipped
--- PASS: TestBootstrap (0.00 seconds)
PASS
ok books 0.025s
成功!
将Specs标记为失败
虽然您通常希望使用匹配库(如Gomega)在您的Spec
中进行断言,但Ginkgo提供了一个简单的全局Fail
函数,允许您将Spec
标记为Fail
。只需调用:
Fail("Failure reason")
Ginkgo 将会处理其余的部分。
Fail
(因此Gomega,因为它使用Fail
)将为当前的space
和panic
记录失败。这允许Ginkgo停止其轨道中的当前Spec - 没有后续的断言(或者任何代码)将被调用。通常情况下,Ginkgo将会补救这个Panic
本身然后进行下一步测试。
然而,如果你的测试启用了goroutine
调用Fail
(或者,等效地,调用失败的Gomega断言),Ginkgo将没有办法补救由Fail
引发的Panic.
这将导致测试套件出现Panic
,并且不会运行后续测试。要解决这个问题,你必须使用GinkgoRecover
拯救Panic
。这是一个例子:
It("panics in a goroutine", func(done Done) {
go func() {
defer GinkgoRecover()
Ω(doSomething()).Should(BeTrue())
close(done)
}()
})
现在,如果doSomething
返回false
,Gomega将会调用Fail
,这将会引起Panic
,但是defer
的GinkgoRecover()
将恢复所述Panic
并防止测试套件爆炸。
有关Fail以及使用除Gomega之外的匹配器库的更多详细信息,请参阅使用其他匹配库部分。
Logging输出
Ginkgo提供了一个全局可用的io.Writer
,名为GinkgoWriter
,供您写入。GinkgoWriter
在测试运行时聚合输入,并且只有在测试失败时才将其转储到stdout
。当以详细模式运行时(ginkgo -v
或go test -ginkgo.v
),GinkgoWriter
会立即将其输入重定向到stdout
。
当Ginkgo测试套件中断(通过^ C)时,Ginkgo将发出写入GinkgoWriter
的任何内容。这样可以更轻松地调试卡住的测试。
当与--progress
配对使用时将会特别有用,它指示Ginkgo在运行您的BeforeEaches
,Its
,AfterEaches
等时向GinkgoWriter发出通知。
IDE 支持
Ginkgo用命令行运行最佳,ginkgo watch
可以在检测到变化时轻松地在命令行上重新运行测试。
Sublime Text有一组Completions(仅使用Package Control来安装Ginkgo Completions
)和VSCode(使用扩展安装程序并安装vscode-ginkgo)。
IDE作者可以将GINKGO_EDITOR_INTEGRATION
环境变量设置为任何非空值,使专注的Spec
能够显示覆盖范围。默认情况下,如果确定关注的spec不通过CI, Ginkgo 将会Fail,使用非零退出码。