天天看點

實戰 TDD (2): Tasking to Action

B站視訊版

如果給我一個小時來砍樹,我會先花 20 分鐘來磨刀。

​ ---- 林肯

實戰 TDD (2): Tasking to Action

在上一篇内容 TDD 實戰(1) 通過一個案例來展現了 TDD coding 的過程,編碼之前首先要做的事情就是確定已經準确了解了需求。TDD 也不例外如果需求不能擷取準确後續用什麼實踐都無法完全傳遞想要的。

工作中有 BA 人員來挖掘需求,DEV 将業務需求轉化為軟體功能,其中 Tasking to Action 是開發之前的利器,也是 TDD 過程的第一步。

Tasking to Action 能夠幫助開發人員确定自己了解了業務需求,并将需求分解為若幹個可以小步實作的任務,工作中在做卡之前,首先就是對要做的卡進行 Kick-Off,Kick-Off 的過程中 DEV、BA、QA共同進行的,DEV 可以針對分解之後的Tasks 與 BA、QA 達成使用者故事卡了解上的共識,并調整 Task,進而確定需求的實作是建立在共識下的。

是以,Tasking to Action 能夠很好的将品質驗證前置,并為品質内建提供幫助。

實戰 TDD (2): Tasking to Action

01,如何進行Tasking to Action?

當解決一個複雜的問題的時候,你會怎麼做?是不是先将複雜的問題進行拆解,生成一個個簡單的問題,當一個個簡單問題解決,那個複雜問題也就解決了。

Tasking to Action 就是對問題進行拆解的過程,将業務需求分解為有上下文、有行為、有結果的若幹個 Task。每個Task 都是簡單的、能夠快速實作的、代表具體場景的。

實戰 TDD (2): Tasking to Action

在做 Tasking to Action 的過程中發現遺漏的、不确定的需求,User Story 的上下文以及涉及到的邊界,能夠幫助我們更加準确的進行Tasking 過程,避免遺漏。

實戰 TDD (2): Tasking to Action

Tasking 之後的生成的 Tasks 我們可以通過 BDD 的方式來進行描述。

02,Tasking to Action 使用 BDD

工作中我們要描述清楚一件事情,通常會介紹四部分内容:

什麼背景下?

遇到一個什麼問題?

我的解決思路是什麼?

這件事的主要幹系人是誰?

通過這樣的格式我們能夠輕松的完整的讓其他人了解清楚的來龍去脈。

BDD 提供了一種良好的描述資訊的格式,能夠清晰的描述一個使用者故事的某個場景的上下文和結果。就是我們常說的 Given、When、Then。它們代表了不同的資訊Context、Event、Outcomes,如下圖所示是一個機車打火的過程。

實戰 TDD (2): Tasking to Action

為了進一步說明,我們舉個例子,需求如下

"我需要将大象裝進冰箱。”
           

拆分後結果如下:

01. Given:一隻大象
			And 一個比大象體積大的冰箱
			And 冰箱門打開;
		When:将大象裝進冰箱;
		Then:大象被裝進冰箱;

02. Given:一隻大象
			And 一個比大象體積大的冰箱
			And 冰箱門關閉;
		When:将大象裝進冰箱;
		Then:裝載失敗;

03. Given:一隻大象
			And 一個比大象體積小的冰箱
			And 冰箱門打開;
		When:将大象裝進冰箱;
		Then:裝載失敗;

04. Given:一隻大象
			And 一個比大象體積小的冰箱
			And 冰箱門關閉;
		When:将大象裝進冰箱;
		Then:裝載失敗;
           

使用 BDD 我們能夠清晰的描述清楚任何一個 Task 的背景、每個Task 的動作、每個 Task 的期望的結果。

如果 Given 中的條件比較多,可以使用

And

關鍵詞來連接配接

03,Unit Test with BDD

BDD 在編寫代碼時同樣在三方面帶給我們幫助:BDD 風格的方法名、BDD 風格的代碼塊、BDD 風格的斷言。

(1)BDD 風格的方法名

@Test
void should_got_a_qrcode_when_store_bag_given_a_bag_and_a_store_content_ark_with_10_space() {
   ...
}
           

BDD 風格的方法名同樣包含 given、when、then 三部分,常用的格式是:should…when…given。

采用 BDD 風格的方法名雖然長度長,但是語意非常清晰明了,一眼看上去就知道這個測試的上下文、行為、期望的結果。

(2)BDD 風格的三段式代碼

單元測試的代碼塊同樣可以分為 given、when、then 三部分,如下

@Test
void should_got_a_qrcode_when_store_bag_given_a_bag_and_a_store_content_ark_with_10_space() {
    // given
  	Bag bag = new Bag();
    StoreContentArk storeContentArk = new StoreContentArk(10);

  	// when
    QRCode qrCode = storeContentArk.store(bag);

  	// then
    then(qrCode).isNotNull();
}
           

上面便是我們常用的三段式風格。

​ Given:指的是該測試的上下文資訊

​ When:指的是要值測試的方法的行為

​ Then:斷言,驗證執行結果。

工作中可以直接去掉其中的注釋部分,約定俗成 given、when、then 通過空行來分割。

@Test
void should_got_a_qrcode_when_store_bag_given_a_bag_and_a_store_content_ark_with_10_space() {
  	Bag bag = new Bag();
    StoreContentArk storeContentArk = new StoreContentArk(10);

    QRCode qrCode = storeContentArk.store(bag);

    then(qrCode).isNotNull();
}
           

(3)BDD 風格的斷言

上面的例子或許你已經注意到,斷言部分使用了

then()

這是 一種 BDD 的代碼風格,AssertJ 中提供了這種斷言的風格,可以參考

org.assertj.core.api.BDDAssertions

類中的

the()

這樣的風格的斷言十厘清晰的結果應該如如何的。

04,實戰 Tasking to Action

在 TDD 實戰(1) 中我們圍繞儲物櫃的需求來體驗了 TDD 編碼部分的過程,但是并沒涉及到詳細的 Tasking to Action 的過程,本文我們将基于儲物櫃的需求進行 Task 分解。

實戰 TDD (2): Tasking to Action

需求:

現在一家公司提供儲物櫃(Store Content Ark)功能。

使用者通過将包(Bag)存入到儲物櫃後得到一個二維碼(QR Code),

通過二維碼能夠從儲物櫃取出包。

将該需求進行分解之後,形成下面5個 Tasks:

01 Given: 一個包

​ And 一個擁有10個空間的儲物櫃

​ When:存包

​ Then:存包成功并拿到二維碼

02 Given: 一個包

​ And 一個滿了的儲物櫃

​ When:存包

​ Then:存包失敗

03 Given: 一個存過包的儲物櫃

​ And 一個二維碼

​ When: 用二維碼取包

​ Then:取到自己存過的包

04 Given: 一個存過包的儲物櫃

​ And 一個使用過的二維碼

​ When: 用二維碼取包

​ Then: 取包失敗

05 Given: 一個存過包的儲物櫃

​ And 一個非法二維碼

​ When: 用二維碼取包

​ Then: 取包失敗

通過 Tasking 我們産出了 5 個 Tasks,使用這些 Tasks 我們可以在故事卡 Kick-Off 的時候與 BA、QA 共同确認已經了解了需求。

确認需求後的下一步就是将 Task 轉化為具體的測試代碼,同通過測試小步并驅動出業務實作。詳細的可以參考 TDD 實戰(1) 的驅動過程,也可以關注後續的文章,如何一步步驅動出代碼實作。

05,常見問題

(1)TDD 之前必須先做 Tasking to Action 嗎?

是的。

隻是形式可以針對自己選擇更高效的,我平時使用上面的形式,因為結構和條件都很清晰。有時也會在本子上寫寫畫畫,還會借助二維表等進行邏輯梳理,偶爾也會使用腦圖,甚至簡單的問題在腦中就已經完成了 Tasking to Action… 總之選擇選擇自己認為最高效的。

使用 BDD 的風格産出 Tasks 是一種将思路顯性化的過程,雖然确實有少部分人的思維非常缜密,但是掌握這種可視化的方法能夠讓大家處理問題能夠如虎添翼。

至于是否進行可視化展現,取決于可視化的價值,我們會選擇 ROI 高的來做。

(2)Tasking To Action 的結果必須是用 BDD 的方式嗎?

不是。

如上一個問題中提到的,可以用腦圖,二維圖表等,甚至一段清晰明了的文字。

為此我用 Alfred 來定義了一些模版,省略了部分重複的工作,來讓效率更高。

(3)BDD 風格的方法名很長,必須這樣嗎?

不一定。

首先方法名長但是清晰明了是沒有任何問題的。試想當讀一個抽象的方法名擷取到的有效資訊多,還是讀一個很具象的有效資訊多,在單元測試中我相信是後者對這塊測試代碼描述的更具象,是以長方法名名不是問題,而不能清晰的描述測試意圖則成了問題。至于讀代碼的成本可以親身體會。

但是長方法名可以用一些手段來變短,并消除一部分重複。比如可以用 @Nested 來歸組,這樣重複的上下文,就不需要反複提及,是以方法名也就短了。但是同樣需要能夠清晰描述。

不建議使用注釋的方式來描述意圖。首先注釋和方法名都是展現測試方法意圖的,明知重複何不一次完成。随着代碼的增多,原本的測試有些情況下會變動,注釋往往忘記修改,何不省掉注釋用方法名來展現呢。

(4)常見到 should … when … 少了 given,這樣寫有問題嗎?

不推薦這樣寫。

如果你早就熟練操作方法名,别切由相關理論、實踐支撐,那麼沒有問題。但是如果沒有,隻是照搬别人的寫法,那麼不推薦這種寫法,我試着問過一些用should…when…這樣寫法的同學,首先他們意識到given和when 混合在一起了,其次并不能第一時間說出 when 中的動作是什麼。這就是涉及到讀代碼的成本了,讀代碼花了更多的時間,這些短小的時間偏短後續會積累成很長的時間。

是以推薦使用 should…when…given…這樣的結構。

寫在最後,如果對 Tasking to Action 非常熟悉,我們可以改進其中的步驟,進而提升效率。但是如果對其并不熟悉,例如看到文中的例子,看上去簡單,但是上手的時候卻不得不回來看看例子的,還是老老實實先練習幾次,知道是什麼,為什麼這麼做了,再根據自己的經驗來改進。

繼續閱讀