iT邦幫忙

5

PHP連載 7

單元測試理論

以下CODE都已PHP CodeIgniter框架為例
劇情模式

某天早晨,某鷹的SKYPE....突然叮咚了一下。

大神:
小鷹,因該要教你單元測試了。

小鷹驚訝的回:
大神,什麼是單元測試

大神回:
單元測試就是對每一個功能,做合理的預期測試,有了單元測試,才有辦法做TDD(TESTING DRIVER DEVELOP,測試驅動開發)

小鷹疑惑的回:
那單元測試的目的是什麼阿?

大神:
快速、品質、可維護

大神:
小鷹,你會員驗證會怎麼做。

小鷹很堅定的回:
輸入帳號密碼,然後去資料庫對應資料,然後有資料則...,沒有資料則...

大神回:
果然是一般工程師思維,我跟你講真正的TDD做法,首先創一個函式,
假設叫做auth_account好了。
先測試傳回是不是false,因為你什麼都沒寫應該要是false。
然後傳入帳號後,先假設資料已撈出給驗證,測試這測試是不是正常。
接著做資料的帳密比對,測試驗證傳回的結果正不正常。
最終他應該只會傳回true跟false來決定帳號是否通過。
如果通過時你要取得必要資料,則要另開函式(或物件方法)去測試驗證資料是否取得成功。
當你測試完全無誤後,你的code不用再寫了,因為已經寫好了。

小鷹趁機coding就回:
大神,那我這樣寫是單元測試嗎?

function auth_account($account){
    $query = $this->db->where('account',$account)->get('user');
    if($query->num_rows() > 0){
    return true;
    }else{
    return false;
}
}

大神回:
no!no!no!你這樣就只有函式,但你沒去測試,而測試不是你做而是電腦做,
也就是說你要另外寫個code,去測試你的函式是不是正確的,
而那個code的驗證功能,幾乎所有的framework都有提供。

大神接著回:
給你一個觀念,絕大多數的工程師不寫程式,正是因為他們覺得寫一個code去測試另一個code,
是很蠢的一件事,因為覺得很浪費時間。
我不否認這會讓你codeing的時間變成二倍,不過話說回來,也不過就是codeing的時間。
假設你寫個功能要2個小時,那你可能就要多花2個小時去先寫測試,看起來很花時間對吧!
但是如果真的就只是這樣我就不會跟你提了,重點是假如你最後要花3天的時間debug,
當你做單元測試和測試驅動的話,可能debug只要花你3分鐘。
現在,你知道省時間是省在那了吧!所以單元測試是為了 DEBUG 與 維護方便。
舉個例子來說,如果是二天的工作,可能會花你四天,但是二個月的工作,可能只會花你40天!
甚至只剩4週。

這是小鷹想到什麼就回:
可是一般專案時間已經很緊湊了,所以是不是因為專案時間的關係,
所以很多工程師都沒有做單元測試。

大神回:
正解,大家都會因為時間太趕,就會覺得單元測試是多餘的,沒價值的。
等到軟體交出去後,等到客戶抱怨不好用,再來回頭修bug時,
你會發現工程師就是無止境的加班解bug。

小鷹又回:
不過專案經理跟業務好像不會把單元測試歸納到時間內

大神回:
這也是正解,因為沒有人會想到是用測試來寫程式,
而不是寫完再來測試,最主要的是,
每個工程師的「垃圾」回收機制不同,大部份的工程師,
都不做資源回收的,我這樣說好了。
一個簡單的5+3,答案是8,但我們要去驗證,
於是你會看到工程師這麼做,echo add(5,3);,然後當答案是8時,
他就把這行mark掉甚至刪掉,他卻不知道被他刪掉的這行才是寶。
因為我們可以另外開測試程式這樣跑

function test_add(){
      $this->unit->run(add(5,3) == 8,'is_true','測試5+3是不是等於8');
}

測試時你可以任意改值去看是不是符合你要的答案。

大神回:
好了我要去開會了!自己領悟吧!

小鷹心裡想趕coding都來不及了,哪有時間在單元測試[某神 神踢 小鷹飛了]

這個劇情告訴我們心裡OS不要亂說出來(某神 巴頭),
說錯了是告訴我們單元測試對程式的維護與DEBUG來說是很重要,
所以做了單元測試只是會消耗一點coding時間,可是呢!
會節省DEBUG的時間與維護的時間。


0
總裁
iT邦好手 1 級 ‧ 2014-01-13 15:40:49

老鷹確定自己有聽懂神話嗎??...暈

0
Tina Lee
iT邦新手 3 級 ‧ 2014-01-13 17:21:55

之前有一個奉行TDD的開發團隊說,這像倒吃甘蔗,未來功能的維護與改版會有很大好處。
雖然想像也是這樣
但在寫code之前先寫測試的code,多數人不知道怎麼開始。
剛剛翻找了一下,鐵人賽由hatelove撰寫的TDD介紹文章,相當值得參考

0
賽門
iT邦超人 1 級 ‧ 2014-01-13 17:50:05

那...測試的Code要怎麼確定是正確的?撰寫測試的測試的Code?

看更多先前的回應...收起先前的回應...

基本上幾乎每一套framework都會有提供單元測試的功能。
重點是在於你願不願意信任他。
不願意的話,就把測試值通通丟進去看他回應給你的正不正確。
正確的話就是可以信任的測試環境。
然而單元測試中最容易被忽略掉的不是單元測試這件事。
而是工程師會不會把「垃圾code」回收到單元測試去當測試值。
我相信很多人都在做單元測試。
但我不知道有多少人會把他刪掉的code或是mark掉的code回收到測試去就是了。

感謝大神補充謝謝筆記

我才不是什麼大神咧。
純出來騙吃騙喝的。

fillano iT邦超人 1 級‧ 2014-01-14 15:22:19 檢舉

測試的code是否正確,得靠兩件事:

  1. coding standard:需要規定哪些東西需要測試,至少要測試什麼(這不是TDD...)
  2. code review:確認測試是否達到基本要求

有問題的測試程式,意味著本身也會出錯,導致測試無法通過,所以並不需要靠寫測試程式來測試測試程式。不過測試程式撰寫的品質與測試是否有效等等,還是需要利用像code review這樣的活動才能確保。否則我只要寫一堆assert(true)所有的測試就通過了XD

原來如此,PHP常常為了DEBUG,程式老是經常加上又拿掉,雖然想要作DEBUG開關來一次全開關但是資訊太多的話又模糊重點。所以放在單元測試的話就可以省下很多麻煩的程序了。

0
fillano
iT邦超人 1 級 ‧ 2014-01-14 15:04:59

剛好最近CodeData網站有一篇文章可以相互參考:
http://www.codedata.com.tw/java/unit-test-the-way-changes-my-programming/

另外,大推tinalee提到的91大的鐵人賽系列作,這系列是我當屆最喜歡的。

是不是? 是不是? (撥瀏海)

賽門 iT邦超人 1 級‧ 2014-01-14 16:03:46 檢舉

tinalee提到:
是不是? 是不是?

不小心按到tinalee的iT邦檔案網頁,發現您是元老級邦友,在2008年就加入iT邦了。
可是中間有將近六年沒有在iT邦回應、分享、回答....
能說看看你到那裏去了嗎?

simon581923提到:
能說看看你到那裏去了嗎?

麥問,哩A驚

0
wordsmith
iT邦高手 1 級 ‧ 2014-01-15 15:07:14

我覺得測試最難的就是決定要測什麼啊?

不過TDD的確是個好的方式,前面多花點時間,提升code的品質,也方便後續的維護。

0
fillano
iT邦超人 1 級 ‧ 2014-01-16 11:53:35

可測試性通常也會跟系統不同部分是否緊耦合有關係。例如如果把商業邏輯放在Controller,那就會很難測試。但是如果用例如DAO的方式把商業邏輯從Controller切出去,Controller只呼叫DAO的方法,那商業邏輯就可以對DAO來獨立測試。

記得Alan Kay曾經在一個maillist中提到,在物件之間傳遞的「訊息」才是OOP的重點,不是類別繼承或是原型繼承。單元測試所測試的,也就是這些訊息。如果沒有有效的訊息,就無法做有效的單元測試。例如,我們可以這樣設計Controller:不接收參數,因為假設了router會依照url規則來正確呼叫他。不返回結果,因為可以用view engine直接output。這樣的Controller可以運作,但是無法作單元測試。如果改成這樣:

<pre class="c" name="code">
class module_a extends Controller
{
  public function action_a($Request, &$Response) 
  {
    ........
    $value = $model->getValue();
    $Response->view->assign('value', $value);
    ........
  }
}

雖然action_a沒有返回值,但是透過傳入的$Response物件中的view,還是可以檢查action_a的運作是否正確,所以還是可以針對Controller作單元測試。

簡單說,好不好測試這件事,關係到framework的設計以及自己的設計。不過以web的分工來說,商業邏輯是最需要作單元測試的部份,所以能測試的話就盡量吧。

我要留言

立即登入留言