來複習一下前一篇提到的 TDD 步驟:
對於沒接觸過 TDD 的人來說,這樣的描述大概是一頭霧水,
所以今天直接用程式碼來說明,讓我們來看一段極簡風的範例,完整程式碼可以在Github取得,用$ git checkout
切換查看。
假設我們要寫一個輸出 從1加到N 的函式
( PHP 版本: 7.3.8 )
<?php
// sum_n.php
assert(sum_n());
?>
『什麼!這樣是哪門子的單元測試?!它現在甚至連 function 名稱都還沒定義!』
第一次看到 TDD 大概會有這樣的疑惑,但這卻是符合步驟 1. 中所描述的「最少量、剛好能運作的測試」。
在每一次的步驟 1. 中,必定會編寫出一個「目前無法通過的測試」,而最小能運作的測試程式就是呼叫該 function 名稱,即使我們連 function 都還沒定義,不能編譯也算是無法通過 (需編譯的程式語言的話)。
Assertion (或稱斷言) assert()
是一種簡易的單元測試語法 [1],在多數的程式語言中都有,若是參數中傳入的值是 False
,會跳出錯誤訊息。
這時執行看看 (簡單在 Terminal 中執行,不運行 Server [2, 3])
$ php sum_n.php
Terminal 上理所當然地出現了錯誤訊息,代表無法呼叫sum_n()
,接著讓我們進入步驟 2.
PHP Fatal error: Uncaught Error: Call to undefined function sum_n() in sum_n.php:4
<?php
// sum_n.php
function sum_n(){
return 1;
}
assert(sum_n());
?>
( 若有下載範例程式碼,用$ git checkout 1a
查看 )
編寫程式後再執行一次,雖然目前函式內還是空的
$ php sum_n.php
很好!這次沒有印出任何錯誤。
目前程式碼還很少,判斷不需要重構,因此直接進入下一個循環。
<?php
// sum_n.php
function sum_n(){
return 1;
}
assert(sum_n(4)==10);
?>
這次我們加入了,加總到 4 實際應該輸出的數字 10 的測試,接著編寫程式。
<?php
// sum_n.php
function sum_n($num){
$result = 0;
for($i = 1; $i<=$num; $i++){
$result += $i;
}
return $result;
}
assert(sum_n(4)==10);
?>
( $ git checkout 1b
)
太好了,sum_n()
輸出了正確的總合,因此通過了測試。
雖然是sum_n()
的輸出已經是正確的總和,但卻是用很沒效率的方式做運算,
於是我們決定來修改一下。
<?php
// sum_n.php
function sum_n($num){
$result = (1 + $num) * $num / 2;
return $result;
}
assert(sum_n(4)==10);
?>
( $ git checkout 1c
)
經過兩個循環,完成了我們的sum_n()
函式。
是不是很簡單呢,一天馬上就學會了使用 TDD,下一篇我們來聊聊這樣違反直覺寫程式為什麼能帶來好處。
/usr/local/php5/php.d/50-extension-imap.ini
檔案中的extension=..imap.so
此行註解掉。 (Ref: PHP from the terminal running very slow on new mac!)我習慣把 Library API的設計放在制定 test case 時,我會想像我會怎樣呼叫這個 API,預期的參數要怎麼處理,聽說這個作法是 API 的 Interface 為出發點 (design to interface).
訂定Interface是設計階段蠻重要的部分,當團隊定好之後就可以讓成員同時開發各自的部分,減少相依性,不過目前我只能示範一個人寫的程式XD,
TDD的寫測試階段應該就算是在制定test case,不過是用編寫自動化測試的方式,並且跟開發穿插在一起。
兩頂帽子的概念就是用在一個人的時候!