單元測試
單元測試在大型應(yīng)用開發(fā)中是非常重要的一環(huán)胰舆。go 自身提供了單元測試框架晶衷,但是原生單元測試框架提供的功能太弱了你虹,所以這里分享下最近研究搭建的單元測試環(huán)境
目標
支持測試與數(shù)據(jù)庫交互抛杨,每個單元測試用例的數(shù)據(jù)庫環(huán)境必須都是要干凈的
支持斷言
簡單的在測試中生成數(shù)據(jù)
為了達成這些目標暗赶,我們可以使用一些現(xiàn)成的go第三方包來幫我們
testify
https://github.com/stretchr/testify
testify
用go實現(xiàn)的一個assert風(fēng)格的測試框架鄙币,這個包提供了我們需要的斷言的功能,提供了非常豐富的斷言方法。
assert.Equal(t, 123, 123, "they should be equal")
assert.NotNil(ret, "")
同時testify也提供了如mock這種的功能蹂随,如果有童鞋需要十嘿,可以自己去翻閱文檔https://godoc.org/github.com/stretchr/testify/mock
重點來了
testify
提供了suite
包提供了類似rails minitest中可以給每個測試用例進行前置操作和后置操作的功能,這個方便的功能岳锁,在前置操作和后置操作中去初始化和清空數(shù)據(jù)庫绩衷,就可以幫助我們實現(xiàn)第一個目標。
同時,還可以聲明在這個測試用例周期內(nèi)都有效的全局變量
type ExampleTestSuite struct {
suite.Suite
VariableThatShouldStartAtFive int
}
// 每個測試用例執(zhí)行前都會調(diào)用
func (suite *ExampleTestSuite) SetupTest() {
test_helpers.Init(config.Cfg)
}
//其中一個測試用例
func (suite *ExampleTestSuite) TestExample() {
assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive)
}
// In order for 'go test' to run this suite, we need to create
// a normal test function and pass our suite to suite.Run
func TestExampleTestSuite(t *testing.T) {
suite.Run(t, new(ExampleTestSuite))
}
// 每個測試用例執(zhí)行后都會調(diào)用
func (suite *ExecutorTestSuite) TearDownTest() {
test_helpers.CleanTables()
}
dbcleaner
https://github.com/khaiql/dbcleaner
有了前置操作和后置操作咳燕,我們就可以想辦法來保證每個用例的數(shù)據(jù)庫狀態(tài)都是干凈的勿决。最簡單的辦法,就是在SetupTest方法中聲明數(shù)據(jù)庫招盲,然后用例操作完數(shù)據(jù)庫玩后低缩,在TearDownTest()方法中truncate數(shù)據(jù)庫。
但是曹货,go test的時候咆繁,是多個協(xié)程一起跑的,如果簡單這樣做,有可能導(dǎo)致測試時數(shù)據(jù)庫出錯控乾, dbcleaner
可以幫助我們避免這個問題么介,這個包是模仿ruby中的database_cleaner
的功能。使用方法如下
在測試中有可能用到的表蜕衡,先聲明加鎖
Cleaner.Acquire("users")
然后在用例結(jié)束后
Cleaner.Clean("users")
配合suite
壤短,就可以保證數(shù)據(jù)狀態(tài)的干凈。
同時建議慨仿,為了減輕些測試的心智久脯,最好全局定義有可能用到的所有的表,然后在每個測試用例镰吆,同意調(diào)用一個封裝好的函數(shù)
factory
https://github.com/nauyey/factory
有了上面兩個庫帘撰,已經(jīng)完成我們第一和第二個目標了,第三個目標同樣非常重要万皿,測試用例中摧找,生成數(shù)據(jù)是非常重要的,如果牢硅,每次都調(diào)用model層的方法來生成數(shù)據(jù)蹬耘,非常繁瑣,因為生成的測試數(shù)據(jù)减余,有很多字段综苔,并不是我們關(guān)注的,但是用model去生成位岔,有很多時候如筛,必須遵循驗證規(guī)則,不得不去聲明一些字段抒抬,所以factory就非常重要了杨刨,同時factory也是各種測試體系中不可或缺的一部分.
factory
這個庫,參考的是ruby中factory_bot
這個庫擦剑,使用ruby寫測試過的同學(xué)妖胀,絕對都使用過這個庫可免,go中的factory
庫,實現(xiàn)了大部分功能
userFactory := def.NewFactory(User{}, "db_table_users",
def.SequenceField("ID", 1, func(n int64) interface{} {
return n
}),
def.DynamicField("Name", func(user interface{}) (interface{}, error) {
return fmt.Sprintf("User Name %d", user.(*User).ID), nil
}),
def.Trait("boy",
def.Field("Gender", "male"),
),
)
定義好了factory后做粤,使用就非常簡單了
user := &User{}
Create(userFactory, WithTraits("boy")).To(user2) // saved to database
這樣,在測試用例中捉撮,非常的方便怕品,能減輕很多工作量。
把factory定義在全局的某個地方巾遭,就可以配置一次肉康,然后在多個測試用例中使用
go-randomdata
https://github.com/Pallinder/go-randomdata
這個庫可以生成一些隨機的假數(shù)據(jù),就是測試里常說faker
總結(jié)
有了這些庫灼舍,在加上適當?shù)姆庋b吼和,就趕快開始愉快的寫測試吧!??