What have you found for these years?

2008-08-17

測試的困難

refer: BDD and TDD - What are they for?

對於測試這種事,老實講我真的感到很棘手。其中的複雜度,實在不亞於真實程式...
當然,比起「證明」這種事,測試已經算是很簡單的了 XD
儘管如此,測試仍然是非常困難,尤其在沒有經驗的時候,很難想像期間的複雜。
理論上來說,就只是隨便寫 use case, 驗證正確性而已嘛。

是這樣說沒錯,但是如果寫測試的時間,遠超過寫實際程式,或是「跑」測試的時間、
空間成本太過於昂貴,那... 總之,這種成本與成效的拿捏,實在是非常難掌握。

我第一次寫測試應該是在 ludy 裡面吧?就是用 ruby 最單純的 test/unit.
由於 ludy 本身只是希望可以擴充一些 ruby 的 method, 或是後來放一些小的 lib,
這些東西本身就沒有任何 dependency, 測試寫起來可以超快,執行速度也超快。
所以根本就是非常輕鬆愉快...

後來應該是試著在 rails 裡面寫 unit test 吧?這部份就很頭痛了,
我有點忘記當時是什麼樣的情況了。總之,我會一直碰到 spec 本身就在改變,
結果我 test case 要重寫,然後程式又要重寫。如果我只是改了一個小差異,
實際程式可能只改幾行而已,但是 test case!!! 就要像發瘋一樣一大堆地方要改寫。

test case 會出現一些重複性應該是難免的,不然測試是要測試什麼?
假設我們要測試 factorial, 我們總不能這樣寫吧?

5.times{ |i| assert_equal factorial(i), factorial(i) }


這樣有寫跟沒寫根本就一樣。
所以必然會變成:

answers = [1, 1, 2, 6, 24]
5.times{ |i| assert_equal answers[i], factorial(i) }


然而假設現在 factorial 定義變成 factorial*2, 實際程式就把最後答案 *2 即可,
但測試的資料就要手動修改成 [2, 2, 4, 12, 48]...

類似這樣的狀況,我在 rails 裡頻頻碰到。寫久了就覺得,我好像根本就在浪費時間...
而且這本來就是一次性的程式,時間才是重點... 再加上 rails 程式很難測試,
牽涉到的層面太廣,很多東西需要假造。於是又會碰到,那這個 fake/mock/stub 又
究竟對不對呢?難道又要替他再寫一份 test? 這樣究竟要寫到什麼時候?

搞到最後我測試根本不想管了,就放給他爛,反正程式跑起來是對的就好了...
唯一的優點只有在開發初期抓出了一些邏輯上的錯誤,不過這個用 browser
測應該也看得出來。這次的經驗,大概算是失敗一半也成功一半吧...

*

至於 c++ 的部份,那又更是不知道要怎麼測試了。ruby 要假造東西很容易,
c++ 實在非常麻煩又困難,再加上又要 compile, 使得我根本懶得去試這種東西。
不過值得高興的是,static typing 如 c++, 能 compile 過,其實也等同於
一部分的測試已經過了。我想這也是 ruby 極需 testing 的原因吧。

*

flash action script 不要問我怎麼測,好像沒聽過有人在測的...
不過看 adobe 極欲把他推上 programming 檯面,我猜現在應該也有不少工具了。
有機會的話再來看看要怎麼弄吧。老實講,as 測試的需求可能還比 ruby 高!
ruby 我不太會踩到什麼地雷,as 踩到的地雷早已無以數計了...
as3 有進步就是了... 雖然有時候又變得過於 verbose. 還是 ruby 好。

*

下一個有寫測試的印象,似乎就是近期的 facebook compatible api 了。
這部份我就覺得算是相當成功的測試案例了。引入 mocha 使得偽造資料變得極為容易。
不過有些部份,老實講非常難 mock, 因為他跟整個 rails 程式有關 @@

這部份,也就是 photos.upload 這邊,我乾脆不假造任何東西,真的開 server 拚了。
因為這部份需要動到 rails 處理 POST data 的動作,而且也需要特殊的
Net::HTTP.post_multipart_form =_=b 這我大概是從 facebooker 借來的吧。

與其環境搞半天,乾脆就開假 server 來接資料。這邊實在很想罵 facebook,
搞這什麼莫名其妙的 spec, 要改的地方還真是有夠多。

這邊牽扯到的東西實在很多,除了要上線的程式我自己的電腦沒辦法跑,
dependency 實在太多了!domain name, ip, database, memcached 現在好像沒跑。
總之雜七雜八的一堆,一定要造假。所以就寫了一堆奇奇怪怪的 fake,
由於 api 暫時還不公開,還要檢查來源 ip 是不是 private call,
要是沒有 mocha, 這測試寫下去應該會瘋掉吧...

值得高興的是,後來我為了修正一些小地方,重新跑 test, 偶爾會抓到 bug.
而且數量其實還不少... 所以我覺得這次的 unit test 就很有用。這也幸虧是
facebook api spec 不會亂動,哈哈。所以 test case 裡面無數 verbose 的 xml,
幾乎很少有機會需要去動到。這點真的太值得高興了。不然一直修 xml 真的會瘋掉。

*

到這邊我就一直在思考 rspec 到底有沒有用。TDD 和 BDD 到底有什麼差異?
到底哪一個比較好?之前讀 Martin Fowler 的文章,加上閱讀其他 mailing list 上的
發言,大抵上來說,兩者其實確實是非常近似的。Martin Fowler 說他自己可能會繼續
stick on TDD, 因為他看不太出來有迫切換成 BDD 的需要。基本上,我的感覺跟他類似。

老實講,我真的覺得 BDD 很像 TDD, 再加上,總覺得 BDD 也很 verbose,
有點像是 TDD 的一種特例似的。讀了一些別人對 mock 的看法,再加上 mocha 的實作,
感覺 BDD 像是要針對流程進行測試,不過我非常不喜歡這樣。因為我認為流程應該是要
有彈性的,像是實作細節,本來就應該要可以隨時調整。如果針對實作細節測試,
那不是造成測試又要重寫?

所以我仍然是比較 prefer 做一些動作,然後針對輸出結果進行測試,而不是驗證行為
是否依照指定的順序而執行。如果真的需要這麼做的話,這似乎比較像是另一個層級的測試,
例如,某些程序上的作業之類的。如果是寫 library 層級的程式,感覺是不太會碰到這些。

因此我一直在懷疑究竟有沒有必要從 test/unit 轉到 rspec?
放眼望去,幾乎所有 ruby project 都跑到 rspec, 或是 rubyspec 的 mspec,
或是 bacon 等各種 rspec clone...

我發現對於技術方面,我也滿愛追求流行的。只是有些東西還是讓我深感困惑,
rspec 就是其中之一。我一直對 it "should go to sleep" do 這種奇怪的東西,
感到非常感冒。而且什麼 should.not.be.an.apple 之類的,這真的看了會想打人,
尤其是如果看到 an 的定義就是 self, 那... .an.an.an 又是什麼意思?

之前引用過一篇,就是 Dave Thomas 在批評 rspec 的這種作法。
This is not longer a DSL. It's broken English.

由於相當有同感,不禁想叫好。這是我一直沒有換 rspec 的最大原因。
另一個原因則是,我看不太出來 rspec 比起 test/unit 有多出多少東西?
如果說是 mock support, 我大可用 mocha 啊,何必全部都用同一套 lib.

我自己覺得 rspec 比較有優勢的是 nested test, 更好的 hook,
漂亮的輸出畫面,大概就這些吧。只是單單為了這些就換 rspec, 感覺也沒有意義。
所以就還是一直在用 test/unit.

但老實講,對 test/unit 我也真的是相當不滿意,希望可以換。
無奈真的沒看到什麼比較完美的 library 可以一次全部取代掉...
可能先試試看 test-spec 之類的吧?作者是 bacon 的作者,應該值得一試。

或是乾脆自己開始做一份算了?反正也不是很難寫的東西,都有 miniunit 了。
說不定是個好主意,省得看什麼都不順眼結果一個都沒用...

*

接下來大概就是 dm-mapping 了吧。這東西的狀況跟 ludy 接近,
dependency 不高,乾脆就直接拿正式的東西來跑。尤其需要測試的結果都在
database 上,難道我需要 fake 整個 database? 這工程實在是太大啦...
不過不曉得其他 adapter 實作者是怎麼測試的,有機會應該來參考一下的。
現階段沒幾個 test 要跑,sqlite3 + mysql 也才花 2 秒,應該繼續跑
real test 即可。

至於到底要不要換掉 test/unit 呢?真是個棘手的問題啊...

5 retries:

波卡 said...

以後應該把DSL正名成DSPL, Domain-specify Programming language...

我也很受不了RSpec那種奇怪的語法 Orz

godfat 真常 said...

我同意 XDD
還好喜歡那套的人似乎沒有那麼多
for a real programmer, i suppose?

==
plumm 的晚點回,那好長 XD

Plumm said...

for_a.real.programmer "i.suppose?"

這本 RealWorld Haskell 是我蠻看好的,目前在 Beta :QQ
http://book.realworldhaskell.org/beta/

最近在 RA3 Beta 都打不贏別人 @.@

Plumm said...

http://www.ntnu.edu.tw/frcenter/presentation/eurolan.html

godfat 要的拉丁文課程找到了 :QQ

Plumm said...

昨天拿到魔戒 Battle for the Middle Earth II 及資料片,也算有點年代了,不過還是過來說說感想吧。

一代時我玩魔多(Mordor),這是比較冷門的陣營,我打個比方來說,像 VM Japan 好用的中階幻魔如茶壺扇魚,只給你低階的拓鼓水鶴和高階的豪天照龍蝦的話呢?這就是魔多的感覺,魔多其實不容易玩,因為它的初階單位,雖然便宜,但造的時間並沒按比例縮短,而且二隊去打別人一隊常常還只是送經驗 (這遊戲部隊有經驗值的),而高階單位又是 Overcosted,等你生出來一隻,對方也一堆單位出來了。

其實魔多的標準打法是用投石車或是投車 Troll 等單位在後面打,前面用滿滿的廉價單位擋住,投石車打過去通常命中的單位都可以直接掛,我方的廉價單位被打中不痛不癢,但是對方的被打掛它可就心痛了,所以一代時直衝投石車是很常見的打法。

二代時,我覺得直衝投車風險蠻大的,還是先把 Troll 生出來,Troll 其實算蠻萬用的單位,雖然軟了一點,但是武器可以切投石,拳頭還是拔樹讓它有利,平常時跟著廉價兵後面投石,對手接近時切換成拳頭,拔樹不太常用就是了。

不過重點是 Troll 出來之前,還是天賦點魔眼看對手的形勢較保險,不過我一般是習慣點腐化土地,這是一個永久效應,我方單位只要位於這土地上,可以得到 +50% 攻 +50% 防。

忘了說二代從四個陣營加到六個陣營,資料片又再多一個(不過有人說資料片改 INI 還可以再改出來一個)。正義方有 : 人類、矮人、精靈、加一個改出來的。邪惡方有 : 艾辛格(白胞薩魯曼在這一族)、魔多、哥布林、安格拉(?)。

基本上要對抗其他種族,要看其他種族的優勢劣勢為何,人類強項是騎兵,精靈是弓兵,矮人是單位都很硬但是移動慢,艾辛格一代時是反制兵很強力,這代似乎是步兵不錯狼騎也強還有弩車的汎用型種族,魔多就是上面介紹的,後期有戰象和攻擊型Troll(不同於前期的汎用型Troll),還可以海戒靈。哥布林是打海的,比魔多的低階單位更能海又強力。安格拉是有召喚師和法師的陣營。那個可以改出來的我還沒試過就是了,應該是官方設計隱藏的但是不知為何沒公佈的。

Post a Comment

All texts are licensed under CC Attribution 3.0