针对如何有效测试Go码,阐述了单元测试,并针对两大难点:去耦、依赖,提出了面向接口、mock依赖的解决方案。 此外,本文还讨论了Go领域的实用测试工具,欢迎读者阅读。
单元测试的重点是代码逻辑单元,一般是对象或具体的函数。 我们可以编写足够的单元测试来确保代码的质量,在修改功能和代码重构时,足够的单元测试用例可以给我们足够的信心。
单元测试上面有开发规格。 敏捷软件开发有两个常客:测试驱动开发(Test-Driven Development,TDD )和行为驱动开发(Behavior-driven development,BDD )。 它们既是实践和技术,又是设计方法论。
TDD TDD的基本思想是通过测试推动整个开发,原则上在开发功能代码之前编写单元测试用例。 包括以下五个步骤。
开发人员首先写测试用例
运行这些测试,但这些测试显然会失败,因为测试用例的业务逻辑还没有实现
实现代码详细信息
如果开发人员成功实现代码,他们将执行并通过所有测试
及时重构业务代码,如果新代码的功能不正确,相应的测试文件也会失败
如果需要开发新功能,请重复上述步骤。 流程图如下图所示
tdd-flowchart有一个名为learn-Go-with-tests的Github仓库,目的是通过go学习TDD。
BDD TDD侧重于开发,通过测试用例规范地约束开发者编写更高质量、更少bug的代码。 BDD侧重于设计,在设计测试用例时提倡定义系统,用通用语言描述系统行为,推动系统设计与测试用例相结合进行开发工作。
BDD从TDD派生,主要区别在于测试的描述。 BDD用更易懂的文字编写测试用例,关注的是需求的功能,而不是实际的结果。
像读BDD给定的句子一样读测试的能力带来对测试认知的转变,对我们如何更容易写测试有帮助。
GinkGo Ginkgo是一个go语言的BDD测试框架,旨在帮助开发人员创建具有表现力的全面测试。
GinkGo集成了go本机测试库。 这意味着您可以在go test中运行Ginkgo测试套件。 它还与断言、mock工具包testify和富测试集go-check兼容。 但是,Ginkgo建议与gomega库一起使用。
下面,让我们使用Ginkgo感受BDD模式的测试代码。
使用go get获取的下载
$ gogetgithub.com/onsi/ginkgo/ginkgo $ gogetgithub.com/onsi/go mega/.此命令用于获取ginkgo,并将ginkgo可执行文件转换为$ ggggo
创建套件以创建gopher库
$cdpath-to-package/Gopher在gopher.go文件中提供了gopher结构和验证方法Validate
packagegopherimport (errors (unicode/utf8 ) ) typegopherstruct { namestringgenderstringageint } func validate ) ggopher='。 IFG.age18 ) {returnerrors.New年龄太小,18 ) }returnnil} )我们通过ginkgo bootstrap命令
$ ginkgobootstrapgeneratinginkgotestsuitebootstrapforgopherin : gopher _ suite _ test.go此时在gopher.go类目录中
package gopher _ test import (' testing '.' github.com/onsi/go mega '.' github.com/onsi/go mega ' ) functeststgophop 也可以是命令go test或ginkgo。
$ gotestrunningsuite : gopher suite===================gotestrunningsuite 3360 gopher suite========================gotestestestestestttt
=======Random Seed: 1629621653Will run 0 of 0 specsRan 0 of 0 Specs in 0.000 secondsSUCCESS! -- 0 Passed | 0 Failed | 0 Pending | 0 SkippedPASSok ginkgo/gopher 0.018s当然,空测试套件没有什么价值,我们需要在此套件下编写测试(Spec)用例。
我们可以在 gopher_suite_test.go 中编写测试,但是推荐分离到独立的文件中,特别是包中有多个需要被测试的源文件的情况下。
创建 Spec执行 ginkgo generate gopher 可以生成一个 gopher_test.go 测试文件。
$ ginkgo generate gopherGenerating ginkgo test for Gopher in: gopher_test.go此时测试文件中的内容如下
package gopher_testimport ( . "github.com/onsi/ginkgo")var _ = Describe("Gopher", func() {}) 编写 Spec我们基于此测试文件撰写实际的测试用例
package gopher_testimport ( "ginkgo/gopher" . "github.com/onsi/ginkgo" "github.com/onsi/gomega")func mockInputData() ([]gopher.Gopher, error) { inputData := []gopher.Gopher{ { Name: "菜刀", Gender: "男", Age: 18, }, { Name: "小西瓜", Gender: "女", Age: 19, }, { Name: "机器铃砍菜刀", Gender: "男", Age: 17, }, { Name: "小菜刀", Gender: "男", Age: 20, }, } return inputData, nil}var _ = Describe("Gopher", func() { BeforeEach(func() { By("当测试不通过时,我会在这里打印一个消息 【BeforeEach】") }) inputData, err := mockInputData() Describe("校验输入数据", func() { Context("当获取数据没有错误发生时", func() { It("它应该是接收数据成功了的", func() { gomega.Expect(err).Should(gomega.BeNil()) }) }) Context("当获取的数据校验失败时", func() { It("当数据校验返回错误为:名字太短,不能小于3 时", func() { gomega.Expect(gopher.Validate(inputData[0])).Should(gomega.MatchError("名字太短,不能小于3")) }) It("当数据校验返回错误为:只要男的 时", func() { gomega.Expect(gopher.Validate(inputData[1])).Should(gomega.MatchError("只要男的")) }) It("当数据校验返回错误为:岁数太小,不能小于18 时", func() { gomega.Expect(gopher.Validate(inputData[2])).Should(gomega.MatchError("岁数太小,不能小于18")) }) }) Context("当获取的数据校验成功时", func() { It("通过了数据校验", func() { gomega.Expect(gopher.Validate(inputData[3])).Should(gomega.BeNil()) }) }) }) AfterEach(func() { By("当测试不通过时,我会在这里打印一个消息 【AfterEach】") })})可以看到,BDD 风格的测试案例在代码中就被描述地非常清晰。由于我们的测试用例与预期相符,执行 go test 执行测试套件会校验通过。
$ go testRunning Suite: Gopher Suite===========================Random Seed: 1629625854Will run 5 of 5 specs•••••Ran 5 of 5 Specs in 0.000 secondsSUCCESS! -- 5 Passed | 0 Failed | 0 Pending | 0 SkippedPASSok ginkgo/gopher 0.013s读者可自行更改数据致测试不通过,你会看到 Ginkgo 将打印出堆栈与错误描述性信息。
总结TDD 和 BDD 是敏捷开发中常被提到的方法论。与TDD相比,BDD 通过编写 行为和规范 来驱动软件开发。这些行为和规范在代码中体现于更 ”繁琐“ 的描述信息。
关于 BDD 的本质,有另外一种表达方式:BDD 帮助开发人员设计软件,TDD 帮助开发人员测试软件。
Ginkgo 是 Go 语言中非常优秀的 BDD 框架,它通过 DSL 语法(Describe/Context/It)有效地帮助开发者组织与编排测试用例。本文只是展示了 Ginkgo 非常简单的用例,权当是抛砖引玉。
读者在使用 Ginkgo 过程中,需要理解它的执行生命周期, 重点包括 It、Context、Describe、BeforeEach、AfterEach、JustBeforeEach、BeforeSuite、AfterSuite、By、Fail 这些模块的执行顺序与语义逻辑。
最后,K8s 项目中也使用了 Ginkgo 框架,用于编写其端到端 (End to End,E2E) 测试用例,值得借鉴学习。
往期推荐
同步原语的基石
你真的懂string与[]byte的转换了吗
Go定时任务库:cron
CPU缓存体系对Go程序的影响
高挑的往事,给力!
机器铃砍菜刀
欢迎添加小菜刀微信
加入Golang分享群学习交流!
感谢你的点赞和在看哦~