jim yeh on 一月 6th, 2010

同人在〈測試驅動開發的精神〉中,提到「Refactoring 並不必然是 TDD 的必要的步驟」的觀點。David Ko 回應他猜測 Steven 是根據 Kent 在《Test-driven Development》一書的前言中提到的說法,來認為測試和 Refactoring 也是TDD很重要的一環。他說:

…here’s what we do: we drive development with automated test, a style of development called Test-Driven Development(TDD). In Test Driven Development:
* Write new code only if an automated test has failed
* Eliminate duplication
……

The two rules imply an oder to the tasks of programming.
1. Red – Write a little test that doesn’t work …
2. Green – Make the test work quickly…
3. Refactor – Eliminate all of the duplication….

Red/green/refactor – the TDD mantra

在這段敘述中,Kent 認為 Red/green/refactor 是 TDD 的三字箴言,是 TDD 這個 practice 重要的工作。

所以 Steven 才會強調在做 TDD 時,"測試是第一步","Refactoring 本身是 TDD 三步之中其中必要的一步"

假如正如 David Ko 所說,測試與 Refactoring 是 TDD 很重要的一環,而來強調 TDD 不應該忽略 Refactoring 的工作,這樣的觀點我可以接受,但同人隨後又看到 Steven 的說法,覺得他自以為是的論點讓我很遺憾。

本人從來沒有指出 TDD 是測試 Practice,亦沒有認為測試是 TDD 的目的。

上面的 "功能" 指代碼的內容規格,寫測試時候就是思考新功能的行為,用測試用例的方式去表達出來。就正如:

AssertEquals(3, add(1,2))

那麼簡單,已經是對 add() 作出規範,投入兩個參數,輸出一個數字作結果,並規範了新 fn 的名字是 "add"。寫這句 Assert 時候就應該思考到 add 有兩個參數,投入 1 和 2 就會得 3 這個數字。

重構是 TDD 其中一步,不存在混淆點,再者,沒有測試覆蓋下的根本不能說成重構;另一方面,先寫代碼後補測試是很痛苦的事情。

如果像閣下文中說:「系統不斷演進及需求不斷改變之下,也可能會使架構或設計愈來愈複雜而變得難以維護」,我則認為是在進行 TDD 時候沒有徹底去重構系統。

我建議閣下好好閱讀 TDD 的書本,Refactoring 是 TDD 必需的步驟,由 Kent Beck 到 Robert Martin 以至其他不太有名的 TDD 書本作者,都指出 TDD 的第三步是重構,而不是 "Refactoring 並不必然是 TDD 的必要的步驟。"

看到「我建議閣下好好閱讀 TDD 的書本」這種不尊重不同觀點的說法,同人根本就不想浪費時間來跟對方爭論。即使耐著性子仔細看他的論點,像那些「沒有測試覆蓋下的根本不能說成重構;另一方面,先寫代碼後補測試是很痛苦的事情。」、「如果像閣下文中說:『系統不斷演進及需求不斷改變之下,也可能會使架構或設計愈來愈複雜而變得難以維護』,我則認為是在進行 TDD 時候沒有徹底去重構系統。」等說法,更讓同人懷疑他對 TDD 的認知,其實是過於偏向理念化而不夠切合實際。

不過,對 David Ko 提出 Kent 認為 Red/green/refactor 是 TDD 的三字箴言的說法,同人倒是覺得有探討的必要。以下分享我在 Facebook 的 Scrum Community in Taiwan 回應 David Ko 的觀點,這些觀點應該可以解釋為什麼測試開發不需要徹底重構;其實重構並不是問題,而是到底什麼叫做徹底?而且如果 TDD 可以徹底重構,那麼代表一開始就可以讓設計一次到位,那寫好的測試程式以後也用不著了,不正是多此一舉?
—————————————————————————————————
柯兄,

我想在點空間,您應該曾看到我與某些只談理論忽略實際的顧問級大師論戰吧?即使當時我和他私下交情還不錯,但看到他用簡單的例子把軟體開發化約成不需要研究 how,或系統分析不需要懂領域知識的觀念,就不得不告訴他,他對軟體開發的認知是不夠全面的。這次對 TDD 實務的分歧點,我想也是差不多的情況。

只不過我不喜歡"我建議閣下好好閱讀 TDD 的書本"這樣的說法,在實務上我看過太多空談理論而不懂思辨問題的人,這種自以為是的論調讓我不想浪費時間回應他,但以下針對柯兄所提來說說我的看法。

單就字面來看,測試是第一步,重構是最後一步,看起來是沒錯,但問題是為什麼呢?測試先行我想應無疑義,我文章也談了很多,在此不再多說。但重構的目的為何?就重構的定義在不改變功能的情況下改善程式結構,以增加程式碼的彈性以利未來增加或改變功能。因此如同那第三步所言,為了去掉重覆性而重構。

但為什麼需要去掉重覆性呢?答案不外乎兩種,一種是日後重覆性造成程式維護的困難與增加錯誤機率,另一種是程式員擔心發生以上的現象或是因為個人對程式設計的信仰所致。

第一種狀況的重構是合理的,因為開發者是針對現實來解決問題;第二種狀況的重構很常見卻不見得是應該被鼔勵的做法,因為開發者是針對理想或信仰來行動,卻不見得符合實際的需要。但這兩種狀況都可以用重覆程式碼的壞味道的概念來合理化重構的行動,所以重構是正確的行動嗎?答案是視情況而定。

一些 TDD 的書籍或教本所舉得例子都很簡單,使得每一步驟都顯得理所當然。但真實的專案真的是這樣嗎?但個人當初在導入 XP 實務的時候,所遇到的困難,都沒辦法從那些書中得到答案,而是要親身體驗;書中教你的 what 其實需要你實施後的 how 及 why,才能更為全面把那些實務做到好。

最後,提一提測試涵蓋面的問題。在我還沒實施 TDD 的時候,當時我很肯定 TDD 的方法,但我想到的問題是測試程式要寫到什麼程度。但等到我開始實施 TDD 的時候,才發現測試程式的涵蓋面根本就不是問題,而是你需要 test driven 幫你做什麼。如果問題具體而明確,那那會有涵蓋面或不涵蓋面的問題?這時,才了解 Peter Ho 及盈學兄當年在點空間說過 TDD 不是測試而是設計的手法。還有那三句擺在我心中的經典名言:

Without refactoring, there is no room for plugging in new functionality to adapt the coming force.

Without testing, we couldn’t know where the edge is.

Without integration, the spark of life will die out.

一點淺見,供參考,但我絕不會叫別人好好閱讀什麼書或文章才能跟我討論…



     

2 Responses to “測試驅動開發要徹底重構?”

  1. [...] 昨天寫的〈測試驅動開發要徹底重構〉,曾經提到 Steven 說到「如果像閣下文中說:『系統不斷演進及需求不斷改變之下,也可能會使架構或設計愈來愈複雜而變得難以維護』,我則認為是在進行 TDD 時候沒有徹底去重構系統。」,同人認為這段話有根本上的邏輯的繆誤,於是回應: 假如上面的話是成立的,那麼代表只要徹底重構系統就不會造成「系統不斷演進及需求不斷改變之下,也可能會使架構或設計愈來愈複雜而變得難以維護」嗎?如果是這樣,那 XP 根本就不需要 Refactoring 這個實務來改善程式的結構,因為你都徹底重構程式,程式結構變差的情況是根本不可能發生。 [...]

  2. [...] 測試驅動開發的精神 測試驅動開發要徹底重構? [...]

Leave a Reply

You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="">