go 用testify搭建完整易用的測(cè)試環(huán)境

單元測(cè)試

單元測(cè)試在大型應(yīng)用開(kāi)發(fā)中是非常重要的一環(huán)。go 自身提供了單元測(cè)試框架,但是原生單元測(cè)試框架提供的功能太弱了,所以這里分享下最近研究搭建的單元測(cè)試環(huán)境

目標(biāo)

  1. 支持測(cè)試與數(shù)據(jù)庫(kù)交互,每個(gè)單元測(cè)試用例的數(shù)據(jù)庫(kù)環(huán)境必須都是要干凈的

  2. 支持?jǐn)嘌?/p>

  3. 簡(jiǎn)單的在測(cè)試中生成數(shù)據(jù)

為了達(dá)成這些目標(biāo),我們可以使用一些現(xiàn)成的go第三方包來(lái)幫我們

testify

https://github.com/stretchr/testify
testify用go實(shí)現(xiàn)的一個(gè)assert風(fēng)格的測(cè)試框架,這個(gè)包提供了我們需要的斷言的功能,提供了非常豐富的斷言方法。

assert.Equal(t, 123, 123, "they should be equal")
assert.NotNil(ret, "")

同時(shí)testify也提供了如mock這種的功能,如果有童鞋需要,可以自己去翻閱文檔https://godoc.org/github.com/stretchr/testify/mock

重點(diǎn)來(lái)了

testify提供了suite包提供了類(lèi)似rails minitest中可以給每個(gè)測(cè)試用例進(jìn)行前置操作和后置操作的功能,這個(gè)方便的功能,在前置操作和后置操作中去初始化和清空數(shù)據(jù)庫(kù),就可以幫助我們實(shí)現(xiàn)第一個(gè)目標(biāo)。
同時(shí),還可以聲明在這個(gè)測(cè)試用例周期內(nèi)都有效的全局變量

type ExampleTestSuite struct {
    suite.Suite
    VariableThatShouldStartAtFive int
}

// 每個(gè)測(cè)試用例執(zhí)行前都會(huì)調(diào)用
func (suite *ExampleTestSuite) SetupTest() {
    test_helpers.Init(config.Cfg)
}

//其中一個(gè)測(cè)試用例
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))
}

// 每個(gè)測(cè)試用例執(zhí)行后都會(huì)調(diào)用
func (suite *ExecutorTestSuite) TearDownTest() {
    test_helpers.CleanTables()
}

dbcleaner

https://github.com/khaiql/dbcleaner
有了前置操作和后置操作,我們就可以想辦法來(lái)保證每個(gè)用例的數(shù)據(jù)庫(kù)狀態(tài)都是干凈的。最簡(jiǎn)單的辦法,就是在SetupTest方法中聲明數(shù)據(jù)庫(kù),然后用例操作完數(shù)據(jù)庫(kù)玩后,在TearDownTest()方法中truncate數(shù)據(jù)庫(kù)。

但是,go test的時(shí)候,是多個(gè)協(xié)程一起跑的,如果簡(jiǎn)單這樣做,有可能導(dǎo)致測(cè)試時(shí)數(shù)據(jù)庫(kù)出錯(cuò), dbcleaner可以幫助我們避免這個(gè)問(wèn)題,這個(gè)包是模仿ruby中的database_cleaner的功能。使用方法如下
在測(cè)試中有可能用到的表,先聲明加鎖

Cleaner.Acquire("users")

然后在用例結(jié)束后

Cleaner.Clean("users")

配合suite,就可以保證數(shù)據(jù)狀態(tài)的干凈。
同時(shí)建議,為了減輕些測(cè)試的心智,最好全局定義有可能用到的所有的表,然后在每個(gè)測(cè)試用例,同意調(diào)用一個(gè)封裝好的函數(shù)

factory

https://github.com/nauyey/factory
有了上面兩個(gè)庫(kù),已經(jīng)完成我們第一和第二個(gè)目標(biāo)了,第三個(gè)目標(biāo)同樣非常重要,測(cè)試用例中,生成數(shù)據(jù)是非常重要的,如果,每次都調(diào)用model層的方法來(lái)生成數(shù)據(jù),非常繁瑣,因?yàn)樯傻臏y(cè)試數(shù)據(jù),有很多字段,并不是我們關(guān)注的,但是用model去生成,有很多時(shí)候,必須遵循驗(yàn)證規(guī)則,不得不去聲明一些字段,所以factory就非常重要了,同時(shí)factory也是各種測(cè)試體系中不可或缺的一部分.
factory這個(gè)庫(kù),參考的是ruby中factory_bot這個(gè)庫(kù),使用ruby寫(xiě)測(cè)試過(guò)的同學(xué),絕對(duì)都使用過(guò)這個(gè)庫(kù),go中的factory庫(kù),實(shí)現(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后,使用就非常簡(jiǎn)單了

user := &User{}
Create(userFactory, WithTraits("boy")).To(user2) // saved to database

這樣,在測(cè)試用例中,非常的方便,能減輕很多工作量。
把factory定義在全局的某個(gè)地方,就可以配置一次,然后在多個(gè)測(cè)試用例中使用

go-randomdata

https://github.com/Pallinder/go-randomdata
這個(gè)庫(kù)可以生成一些隨機(jī)的假數(shù)據(jù),就是測(cè)試?yán)锍Uf(shuō)faker

總結(jié)

有了這些庫(kù),在加上適當(dāng)?shù)姆庋b,就趕快開(kāi)始愉快的寫(xiě)測(cè)試吧!??

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容