如何寫一個好的測試?總結起來就這兩點……

新夢想IT發表於2022-12-26




背景


在上一個專案上,由於專案成員大部分是新入職的同事,所以對於測試不是很熟悉,這就導致了在專案前期,專案上的很多測試都不太make sense,雖然沒有什麼定量的東西來描述,但是總結起來就2個點:


1.測試的名字比較模糊。


2.測試程式碼不易讀。




深入剖析


測試名字比較模糊


對於這一個問題,是因為很多剛開始寫測試的開發腦子裡不會快就想到given、when、then這三個詞,一般我們寫測試寫得比較多的同事,都會使用should_return_xxxx_when(if)_xxxx。其實就是在腦子中想到了given、when、then。而新同事照著模仿的時候,很可能就單純地寫了should_xxx。他們只考慮了結果,但是沒有考慮條件,這就導致了讀名字還是get不到測試想要幹什麼。


對於這個問題,我想起了多年前在stackoverflow上面看到的一種寫測試名字的模板,他是這樣推薦的:


GivenA_WhenB_C,我覺得這中寫法挺好的,所以在專案中用了起來,結合實際使用,我又對此提出了兩個改進:


1.given when這兩個詞可以省略,當然前提是我們約定了最前面就是given,中間就是when,最後就是then。


透過施加規則限制來縮短測試名。


2.測試名字常常很長,一大堆駝峰其實比較不容易閱讀。所以可以使用蛇形命名法,但是這樣就需要想一個符號來分隔given、when、then了,我選擇使用___(也就是3個_),因為經過試驗,發現2個_太短了不容易一眼看出分隔


4個則又沒有必要。


最終的效果是這樣的,這是一個例子,來自我最近的訓練題:


parking_order_is_natural_order___park_cars_by_parking_assistant___car_parked_to_correct_parking_lot_in_turn


car_is_already_took___take_back_car_with_used_receipt___exception_is_thrown


parking_lot_is_available___park_a_car_by_parking_assistant___receipt_returned


car_is_parked___take_back_car_with_invalid_receipt___exception_is_thrown


這裡還有一個小技巧,如果一個測試真的沒有given的時候,或given不重要的時候,可以省略,但是___不能省略:


___xxx_xxx___xxx_xxx




可以總結一下這種命名方式的優點:


1.能制定一個規則的話,專案上的測試標題能更容易統一(可以說是統一語言了)。還可以加上靜態檢查,使得一些名字不規範的測試提前被發現


名字不規範,說明是新進專案的同事寫的,確實要重點檢查一下。


2.強制規定出given、when、then,那麼,我們在寫測試的時候,就會被強迫想清楚我們的測試要做什麼。


3.結構化的東西更適合大腦閱讀,讀測試的時候更容易,我們不需要先讀一遍測試名才能提取到given、when、then,可以一眼就定位出三個部分。




當然,也是有缺點的:


我用這種方式寫出來的測試名字一般都比較長,這個有可能是我用詞還不夠精煉,在given、when的部分可能有時候是有一部分重複的


所以也需要刻意練習,學會精簡用詞。


最後,給這種命名方式命個名吧,就叫GWT測試命名法好了。




測試程式碼不易讀


我在專案上發現,很多人習慣在構建fake資料的時候直接將所有資訊遮蔽,就提供一個createX()的方法,


在createX的方法裡面可能還要構建X的構成部分:


X createX() {


Y yyy = new Y;


X xxx = new X(YYY, field1, field12);


return xxx;


}


除了createX外還有createAX、createBX,然後在不同的測試裡面混合呼叫。


作為看測試的我,我就很難看到到底需要一個怎樣的場景,field1到底是怎麼設定的,field2到底是怎麼設定的,而且在想改一下createX的時候,還會牽扯到其他的測試莫名其妙就掛了。




我目前使用的解決方式是這樣的:


1.先要確定好測試要涉及到的重要資訊,比如上面如果field1,field2都會對測試的邏輯起到作用,


那麼,即使冗餘,也一定要寫在測試方法內。比如:


@Test


void ___xxx___xxx() {


Y y = createY();


X x = createX(field1, field12, y)


// do some thing


// assert some thing


}


這麼做的好處是,當我要看某一個測試的時候,它的前置資料我一眼就能看出來,不需要不停地command+b跟進去才知道需要什麼。


2.在應用第一條原則的時候,一定要記得,只羅列出這個測試所關心的資料。假如field3完全不參與這次


邏輯的處理,又必須要有值,那麼,在createX內部給個預設值即可,不需要放在createX引數列表中。




為什麼不適用builder?


其實之前也試過用builder,但是看起來太多了,適用create的方式能縮短要寫得程式碼行數,更容易一眼看完測試


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69940641/viewspace-2929526/,如需轉載,請註明出處,否則將追究法律責任。

相關文章