golang行为驱动开发(BDD)测试框架插件testcase的使用
Golang 行为驱动开发(BDD)测试框架插件 testcase 的使用
testcase
是一个用于应用 BDD 测试约定的 Golang 测试工具包。
特性
- 轻量级,无依赖
- 支持多种测试风格:经典扁平测试风格、表格测试风格和嵌套测试风格
- 基于领域驱动设计(DDD)的测试组合
- 安全的并行测试
- 可重复的伪随机测试输入生成
- 可重复的伪随机测试顺序打乱
快速开始/示例
基本示例
func Test_withTestcaseT(t *testing.T) {
tc := testcase.NewT(t, nil)
tc.Must.True(true)
_ = tc.Random.String()
}
func Test_withTestcaseSpec(t *testing.T) {
s := testcase.NewSpec(t)
s.Test("", func(t *testcase.T) {
t.Must.True(true)
_ = tc.Random.String()
})
}
带作用域测试变量的示例
package main
import (
"testing"
"go.llib.dev/testcase"
)
func TestMyFunc(t *testing.T) {
s := testcase.NewSpec(t)
s.NoSideEffect()
var (
input = testcase.Let[string](s, nil)
)
act := func(t *testcase.T) string {
return MyFunc(input.Get(t))
}
s.When("input is all lowercase", func(s *testcase.Spec) {
// 没有定义输入的测试作用域会警告开发者缺少规范
input.LetValue(s, "all lowercase")
s.Then("it is expected to ...", func(t *testcase.T) {
t.Must.Equal("all lowercase", act(t))
})
})
s.When("input is all upcase", func(s *testcase.Spec) {
input.LetValue(s, "ALL UPCASE")
s.Then("it is expected to ...", func(t *testcase.T) {
t.Must.Equal("all upcase", act(t))
})
})
}
模块化
你可以将常见的场景设置模块化为辅助函数,这有助于提高测试的可读性,同时将模拟的需求降到最低。
package mypkg_test
import (
"testing"
"my/project/mypkg"
"go.llib.dev/testcase"
. "my/project/testing/pkg"
)
func TestMyTypeMyFunc(t *testing.T) {
s := testcase.NewSpec(t)
myUser := GivenWeHaveUser(s)
// .. 其他给定条件
myType := testcase.Let(s, func(t *testcase.T) *mypkg.MyType {
return &mypkg.MyType{}
})
s.Describe(`.MyFunc`, func(s *testcase.Spec) {
act := func(t *testcase.T) { myType.Get(t).MyFunc(myUser.Get(t)) }
s.Then(`edge case description`, func(t *testcase.T) {
act(t)
})
})
}
稳定性
- 该包被认为是稳定的
- 使用滚动发布约定
- 没有计划对包的导出API进行破坏性更改
- 该包用于生产开发
- 只有在实际用例证明其必要性时才会扩展包API
总结
testcase
提供了一种遵循DRY原则的方式来表达常见的Arrange、Act部分和Asserts。它通过嵌套隐式地可视化你的生产代码所需的心智模型,并允许你将常见的场景设置模块化为辅助函数。
更多关于golang行为驱动开发(BDD)测试框架插件testcase的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang行为驱动开发(BDD)测试框架插件testcase的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang 行为驱动开发(BDD)测试框架 testcase 使用指南
testcase 是一个轻量级的 Golang BDD 测试框架,它提供了行为驱动开发的测试风格,同时保持了 Go 测试工具链的兼容性。
安装 testcase
go get github.com/adamluzsi/testcase
基本使用示例
简单测试案例
package example
import (
"testing"
"github.com/adamluzsi/testcase"
)
func TestAddition(t *testing.T) {
s := testcase.NewSpec(t)
s.Describe("Addition", func(s *testcase.Spec) {
var (
a = s.Let("a", func(t *testcase.T) interface{} { return 0 })
b = s.Let("b", func(t *testcase.T) interface{} { return 0 })
)
act := func(t *testcase.T) int {
return a.Get(t).(int) + b.Get(t).(int)
}
s.When("a is 1 and b is 2", func(s *testcase.Spec) {
a.Let(s, func(t *testcase.T) interface{} { return 1 })
b.Let(s, func(t *testcase.T) interface{} { return 2 })
s.Then("it should return 3", func(t *testcase.T) {
t.Must.Equal(3, act(t))
})
})
s.When("a is 0 and b is 0", func(s *testcase.Spec) {
s.Then("it should return 0", func(t *testcase.T) {
t.Must.Equal(0, act(t))
})
})
})
}
更复杂的 BDD 示例
package user_test
import (
"testing"
"github.com/adamluzsi/testcase"
)
type User struct {
Name string
Age int
}
func TestUserValidation(t *testing.T) {
s := testcase.NewSpec(t)
s.Describe("User validation", func(s *testcase.Spec) {
var (
user = s.Let("user", func(t *testcase.T) interface{} {
return &User{
Name: t.Random.String(),
Age: t.Random.IntBetween(18, 99),
}
})
getUser = func(t *testcase.T) *User {
return user.Get(t).(*User)
}
)
validate := func(t *testcase.T) error {
u := getUser(t)
if u.Name == "" {
return errors.New("name cannot be empty")
}
if u.Age < 18 {
return errors.New("age must be at least 18")
}
return nil
}
s.When("user is valid", func(s *testcase.Spec) {
s.Then("it should pass validation", func(t *testcase.T) {
t.Must.Nil(validate(t))
})
})
s.When("name is empty", func(s *testcase.Spec) {
user.Let(s, func(t *testcase.T) interface{} {
u := getUser(t)
u.Name = ""
return u
})
s.Then("it should fail validation", func(t *testcase.T) {
t.Must.NotNil(validate(t))
t.Must.Contain(validate(t).Error(), "name cannot be empty")
})
})
s.When("age is under 18", func(s *testcase.Spec) {
user.Let(s, func(t *testcase.T) interface{} {
u := getUser(t)
u.Age = 17
return u
})
s.Then("it should fail validation", func(t *testcase.T) {
t.Must.NotNil(validate(t))
t.Must.Contain(validate(t).Error(), "age must be at least 18")
})
})
})
}
testcase 主要特性
-
Let 和 LetValue:用于定义测试变量,支持惰性求值
var count = s.Let("count", func(t *testcase.T) interface{} { return 0 }) var name = s.LetValue("name", "Alice")
-
Before 和 After 钩子:测试前后的准备和清理工作
s.Before(func(t *testcase.T) { // 测试前准备工作 }) s.After(func(t *testcase.T) { // 测试后清理工作 })
-
随机测试数据生成:
t.Random.String() // 随机字符串 t.Random.Int() // 随机整数 t.Random.ElementFromSlice([]string{"a", "b", "c"}) // 从切片中随机选择
-
并行测试支持:
s := testcase.NewSpec(t) s.Test("parallel test", func(t *testcase.T) { t.Parallel() // 测试代码 })
-
表格驱动测试:
s.TestTable("addition cases", func(t *testcase.T) { type TestCase struct { A, B, Expected int } return []TestCase{ {A: 1, B: 2, Expected: 3}, {A: 0, B: 0, Expected: 0}, {A: -1, B: 1, Expected: 0}, } }, func(t *testcase.T, tc TestCase) { t.Must.Equal(tc.Expected, tc.A+tc.B) })
最佳实践
- 使用
Describe/When/Then
结构组织测试,使测试意图更清晰 - 使用
Let
定义测试变量,避免测试间的状态污染 - 为测试案例和断言编写清晰的描述
- 利用随机测试数据提高测试覆盖率
- 合理使用 Before/After 钩子管理测试资源
testcase 框架结合了 BDD 的清晰表达能力和 Go 测试工具链的高效性,是 Go 项目中实施行为驱动开发的优秀选择。