一分鐘懂MockObject
function 外部Class A 裡面有function FA()可以拿到對應值
function 外部Class B 裡面有function FB()可以拿到對應值
我寫的class:
class Example(){
function getA(){
return new A->FA();
}
function getB(){
return new B->FB();
}
function mixData(){
$dataA = getA();
$dataB = getB();
return $dataA + $dataB
}
}
好了,假設這些ClassA ClassB是外部函數,也就是其他人開發的
那我們該怎測試才能確保自己的unit有正常運作呢?
class test_Example(){
funciton test_mixData(){
$mock = $this->getMock('Example');
$mock->method('getA')->willReturn('1');
$mock->method('getB')->willReturn('2');
$this->assertEquals(3, $mock->mixData());
}
}
一分鐘了,應該看懂在寫什麼了吧
看懂以後再看下面
在這樣的具體個案中,如果要確認Unit有沒有"正常運作"
最該care的是取得資料以後, "+ 有沒有執行,有沒有return"
而不該是"AB資料取得的值是否正確"
因此"對這個unit而言",我們不應該做類似以下事情:
"FA可能沒取到值,我們應該assert確認一下"
"FA是否是字串根本不能相加,我們要確定一下是否為浮點數"
這裡要鄭重說明一下,我們的目的終究是要確認這個unit有沒有正常運作
所以,如果你做了外來資料的過度判斷,而到時候又真的出錯了自己不在現場
如果是外來資料有問題,結果unit test檢測被其他人看到,恩經判斷期望結果不符
非常有可能會以為這是你的程式出了問題,結果查好久才查到原來根本是外部程式導致
這會造成大家的麻煩,請務必別做這樣的事情
再來,為什麼這種函數都很少去用各種condition來assert結果
因為:出問題你很難判斷是外在資料錯誤,還是你計算錯(原因可以無限上綱,不要懷疑)[註1]
與其花費大量時間寫各種判斷式來嘗試抓冰山一角
更好的方式就是不要在unit test幹這種事情(不是不測,而是別在這階段做)
(假使要做這件事情,除非程式本身就帶Exception,那我們可以做另一個測試時,帶入不合法的值來確認是否真的會觸發Exception)
Mock Object的作用就是在此,我們應該準確地確認這個unit是否真的有運作
不應該被其他外在函數的特例狀況影響到
這也是單元測試好用的地方,我們即使程式還沒完成或上線前接不到資料
也可以假設某函數資料正確來直接測試,這是Unit Test Framework最強的地方
這樣也不需要在主程式改一些測試專用Code,然後可能上線前還忘記砍掉之類
老話一句,如果想用unit test,
請絕對不要迎合unit test去多做一堆沒用判斷,然後說什麼這樣可能很難測試,我要為了unit test方便而忍痛改變現有架構之類的蠢事
請去用他對你來說真正受益,可以減少程式錯誤,增進開發效率的地方,這樣才能發揮價值
註1:
因為用少數結果絕對無法檢驗程式邏輯有沒有錯
要舉一些極端例子的話,
1.如果你assert 3應該回傳5,我程式如果為了測試,最後一行真的是寫return 5,assert下去絕對有過,但這樣的結果明顯只是鬼打牆(而且這個實際真的有人發生,因為為了寫unit test最後的return還真的忘了拿掉)
2.如果你做了兩個,assert 3應該回傳5 assert 4應該回傳 6 是否裡面剛好程式搞砸了,最後變成 input+2 ,結果assert還是有過
3.然後開始上綱,你的unit判斷式寫太複雜其實有藏了幾個BUG,結果要測目標程式測不到
沒有留言:
張貼留言