2009年9月22日 星期二

寫 Python 測試碼的好幫手

前陣子用 TDD 寫了個六千多行的工具,這篇記錄一下過程中惠我良多的好幫手。

unittest

內建模組。

用法見官網介紹《Dive into Python 3》的第九章 Unit Testing ( 有附帶介紹 TDD ) ,如同 Java 的 JUnit 3.x,用 unittest.TestCase 來寫 test case 很方便。

coverage

安裝方式:easy_install coverage。

Code coverage 是分析測試碼品質的方法,標示出測試碼沒有執行到那幾行程式碼。使用 Code coverage 的基本精神是:Code coverage 的數據高不表示測試碼有效,但數據低的話,測試品質必定不好。至於高低要如何界定?這得看專案的類型,比方科學計算型的程式,要求 80% 以上並不過份;而有一堆圖形介面的程式,可能連到 60% 都很難。

coverage 官網有簡單易懂的使用例子。由於 Python 的功能限制,coverage 無法作到像 Java 那麼全面。coverage 的限制以及 Code coverage 的注意事項,詳見作者的說明:“Coverage testing, the good and the bad.”

nose

安裝方式: easy_install nose。

寫 unittest 時,管理 test suit 是件很瑣碎又易犯錯的事,相信很多人會想說,能不能跑個程式,自行搜集目錄下全部的測試碼並自動執行。沒錯,大家的心聲 nose 聽到了!這裡直接用例子說明 nose 的使用方式:

  • 執行目前目錄下所有測試:
    1
    
    nosetests
  • 執行目前目錄下所有測試並附上子目錄 pkg1、pkg2 的 Code coverage 資訊:
    1
    
    nosetests --with-coverage --cover-package=pkg1,pkg2 --cover-erase
  • 不要執行 slow_test.py:
    1
    
    nosetests -e slow_test.py
  • 使用四個 CPU 平行執行測試:
    1
    
    nosetests --processes=4

–with-coverage 需要先裝 coverage;–process 得另裝 package multiprocessing ( easy_install multiprocessing ),相關說明詳見 Multiprocess: parallel testing

另外,若要讓 nose 跳過物件 A 的測試,就在程式裡寫上

1
A.__test__ = False

比方若不想測模組 mod,就在 mod.py 裡寫上

1
__test__ = False

sqlite3

內建模組。

用法見官網介紹。在測試資料庫時,個人覺得 local database 比 mock 好用,方便準備資料,測起來也比較踏實,而且使用 memory mode 可大幅減少執行時間。附帶一提,sqlite 跨 C、Python、Java 等語言,支援 SQL 92,執行速度又快,相當好用。

PyMox

安裝方式: 用 SVN 從官網 checkout 原始碼,再用 setup.py 安裝:

1
2
3
svn checkout http://pymox.googlecode.com/svn/trunk/ pymox-read-only
cd pymox-read-only/
sudo python setup.py install

用法見官網文件。PyMox 是 Google 開發的 Python 版 EasyMock,我試用過 Java 的 EasyMock 後覺得用法挺直覺的,就決定用它,如此一來學一套工具可以同時用在 Java 和 Python 上。使用 mock 的好處是簡化測試碼,更完備地測試程式,像是代換掉處理資料庫、網路連線的物件。如此一來,連測試「讀資料到一半卻斷網」的應對情形,都是輕而易舉的事。若想立即體會 mock 的功效,不妨配合 mock 用 top-down 的方式作 TDD,會發覺不同的程式開發思維。

2012-08-05 更新: 我現在改用 Mockito 了,更為簡單易用 。

2009年9月20日 星期日

Honey & Clover I, II 觀後感

Honey & Clover
又看了一次 Honey & Clover 第一部和第二部,已經明白劇情發展的我,這次還是一樣被情節給牽動著,忍不住一集集地看下去,而且更能注意細節,更能感受到作者細膩的鋪陳。

看完許久,仍然無法用文筆表達情緒的我,只好照老樣子看著別人的心得,和著劇中插曲讓感覺漸漸褪去。最後的結局,究竟小育是抱著什麼樣的心情,將那份點心交給竹本的呢?

小育想傳達的心意,竹本確實地收到了。一再地重覆看著這段,我不知道自己感受到的是什麼,劇中插曲的歌詞是這麼寫著的:

平靜清澈的溪水,毫不躊躇地向前流淌。
令人懷念的午後熏風,吹拂著濕透的脖頸。
野兔不停奔跑的樣子,白百合的耀眼花姿,
夜空中閃耀的星群,都理所當然地在我眼中。

相信一定會觸碰到的幻影,
充斥著言語的底片中的街道延續著。
再見吧,再見吧,向著不知何時的你。
如果還能遇見你就好了。

只能不斷地聽著插曲,試著沉浸在兩人的心情中,隨手寫出片段的思緒。

小育和竹本彼此的心意,不是友情、愛情之類的字眼可以表示的,那究竟是什麼呢?對於他們而言,這樣的分別又代表什麼呢?為什麼如此的不捨?為什麼如此地感傷?滿腹疑問的我似乎明白答案為何,卻無法述諸言語,或是不願述諸言語。於是不斷重覆聽著插曲、重覆想著兩人的心情、隨手敲著鍵盤,讓這份情感深刻地注入內心深處。對於祝福的人以及被祝福的人都能幸福,感到無比地欣慰,只是感傷也隨著揮之不去。

2009年9月6日 星期日

康特的難題

書籍基本資料:《康特的難題》

看到中後半時,由於相當在意某位角色出奇的應對方式,讓我不得不硬衝了兩小時看到結尾,導致我破例到淩晨兩點才睡,上回這樣挑燈夜戰讀小說,應該是國高中時看金庸小說吧?

若以一般小說來說,算是普普吧。但這本揭開科學界的生活,並用非科學界人士質疑科學界視為常識的文化,還有點出女性科學界的困境,都很有意思,可滿足我對科學界人性的好奇心。像是學界慣於使用的「我們」,究竟意指為何?作者順序的含意?提出想法和執行實驗何者較為重要?實驗室成員是合作者,還是手下?害怕被盜用想法而不敢發表,以及急於發表以追求第一發現者榮譽的衝突?實驗數據不如預期,做些「適當」的調整,是修正誤差還是照假?女性學者在追求終身職和小孩之間的抉擇?書中點出許多與科學誠實、無私、公正、理性、分享等特質矛盾的人性弱點,有許多令人深省的地方。角色的佈局也滿有意思的,將幾位看似無關的核心角色,慢慢地交錯在一起,若起頭和結尾能更圓潤就更棒了。

看完這本書後,讓我更不想走學界路線,我明白自己很難抗拒種種誘惑堅持自己。即使能堅持自己,也很難要求合作者照自己的要求進行,有太多灰色地帶,很難令合作者與自己達成共識。還是遠離利益得失,才能自在地以自己的步調前進。

2009年9月5日 星期六

驗證第一、想法第二

看到這篇《The Only Truly Failed Project》,其中有兩段話寫得很棒:

Failure is a wonderful teacher. But there’s no need to seek out failure. It will find you.

The only truly failed project is the one where you didn’t learn anything along the way.

最近觀察不少數據和實驗結果,推翻之前許多猜想,不過我沒有像碩士做研究時那般痛苦,反而有些興奮。大概是因為沒有發論文的壓力吧,加上先前讀過費曼的言論,覺得這是理所當然的情況,我們知道得本來就很少,想法通常也是錯的。然而,重點在於如何從錯誤的嘗試中學習?

昨天晚上不斷思索這堆錯誤的嘗試告訴我什麼,仍未有定論。忽然想到愛迪生的話,當他實驗千種材料失敗後,他說,至少我明白這千種材料不能作為燈炮的材料。
於是心裡又更踏實了。但這不表示可以隨意的嘗試,在沒有有效的驗證方式之前,無謂的嘗試無法提供任何訊息。

印象中費曼提過,他不和人討論無法驗證的想法,那是沒有意義的行為。提出猜想後,我會思考如何驗證。直到有辦法進行驗證前,我會先保留想法,改試別的作法。或是將原問題先拆成幾個小問題,讓小問題能夠被驗證。待各個擊破搜集到一些資訊後,再回頭看是否能驗證原本的想法。

能夠驗證的想法,才能從中學習。不然結果出來後,我們無從判斷結果有多接近目標,自然也無法從中學習,進而做修正。舉例來說,如果要做 AI 下棋的搜尋,要怎麼知道目前的策略有效?和人下棋是個辦法,可是沒有無法頻覆進行,也就不能確保方法的可靠程度。另一方面,光看評估盤面函式的輸出值也無用,沒法確認分數高確實是有利的盤面。

換個想法,提兩個策略,互相對戰。至少可以確定每次都贏的策略,是相對來說較好的策略。一個策略可以用 greedy algorithm,另一個用自己想的特別方式。雖然我們預期特別的方法會贏,但若結果相反,也能從中明白新東西:像是 greedy algorithm 比想像中的有效,可從中找到好點子;或是特別的方法不如預期地有效,也就不用再和人下棋,減少後續測試的成本。若擔心兩個方法自動對戰變化有限,可以引用不同棋譜的中盤局面,再引入兩個方法擔任不同角色,觀察好的方法是否在各種相同局面裡,無論擔任那一方,都能下得較好。

再舉解魔術方塊的例子。為了能夠驗證,可以先將解好的盤面一步步弄亂,記下正確的還原步驟和所需步數。準備好多組測試盤面(即最後弄亂的樣子),測試演算法解的效果,步數和預期步數相差多少?那一步發展變得不同?於是有確實的數據可以分析,明白問題出在那。有未知的大進步時(只花了預期的一半步數),也方便觀察出原因(如弄亂盤面時做了不當的重覆操作),不會不小心高估方法的成效。

看起來理所當然的事,沒想到我這麼遲才明白,驗證是如此的重要。而這個觀點卻是從 coding 那邊先萌芽,才接著在研究這邊確實實行。不論是研究還是coding,我認為重要性是: 需求 (動機) > 驗證 > 想法 (解法) > 實作。如《管理是什麼》書裡提到,管理即為客戶創造價值 -- 相當含糊的定義,可是卻非常精闢。不論是研究還是寫程式,也要先確保能滿足某種需求,之後的發展才有意義。接著,在天馬行空地想解法前,先確保有方法驗證方法的好壞、達到目標的程度、有辦法分析結果,之後才能確實地落實想法。於是,即使失敗仍能有所收獲。

2009-09-06 Update

LCamel 一提,發現英文的用詞很有趣,Verification and Validation 裡這麼解釋:

It is sometimes said that validation can be expressed by the query “Are you building the right thing?” and verification by “Are you building the thing right?” “Building the right thing” refers back to the user’s needs, while “building it right” checks that the specifications be correctly implemented by the system.

意即:

  • 滿足需求 = validation = do the right thing.
  • 驗證作法 = verification = do the thing right.

語言真是奇妙啊,用一句話來總結,就是「Are the do you right thing right?」