每一個可以努力的日子,都是一份厚禮。
測試驅動開發
測試 是軟件開發中一個必不可少的環節。不管我們是否有意識到,其實我們經常都在做測試工作。比如最原始的在代碼中插入 echo $variable
查看變量值,簡單粗暴而有效。當然,PHP 也會有 XDebug 這樣的工具,可以做到單步調試,不過要在服務器上安裝再配合好本地一個 IDE 和瀏覽器一起運作,始終不是那麼方便。
我們實現了一些複雜邏輯後,總會要輸入一些數據,來確認程序是否能給出預期的結果,頁面是否正常展示。更高級的開發者會寫一些代碼來自動完成這個測試過程,這樣一來每當我們需要測試一些東西的時候,只需要調用測試腳本,就能直接獲知結果了。這就是所謂的 自動測試。
軟件測試已經發展為一個獨立的工種,黑盒、白盒、單元測試、功能測試……流程都比較規範化了。隨着軟件開發過程的不斷演變,各種方法論層出不窮,類似的名詞包括:敏捷開發、持續集成、XP極限編程、結對編程、TDD……一般談這些概念的時候都會被大牛黑出翔來,比如酷殼博主 @左耳朵耗子 發表過這樣一條微博:
不會寫程序的人來搞什麼軟件開發諮詢,SQA,流程設計,軟件項目管理,全是扯蛋。所以,程序員應該要像 Linus 一樣自信的對這些人說:“Talk is cheap, show me the code.”
習總書記教育我們:
空談誤國。
我想聊一下 TDD。我必須聲明我並非想借技術名詞炒作概念。自從上次一篇 關於MVC的討論 被新浪 SAE 的微博大號轉發後,引來口水無數,各種被噴。其實我也只是初學者,談談自己對編程的理解,希望能嘗試一下不同的實踐方式而已,最終想法和所有開發者一樣,都是要提高代碼質量和生產效率。
TDD 簡介
TDD 測試驅動開發,這個概念來自於敏捷中的極限編程(XP),其中的主要觀點即:測試對項目成敗起決定作用。測試應該自動化,經常測,並儘可能在功能代碼還沒實現之前就寫好測試代碼。在 XP 理論中,架構設計應該是恆定的不可隨意改動的,而代碼實現卻會不斷迭代和重構。
所謂的 測試驅動開發 的開發周期:
- 創建一個涵蓋要實現的特性的新的測試。測試預計將在第一次執行的時候失敗,因為特性尚未實現。
- 執行所有測試,確保這個新的測試是失敗的。
- 編寫代碼來使得測試通過。
- 執行所有測試,確保所有測試通過。
- 重構新編寫的代碼並確保這些測試仍然能夠通過。
重複步驟1至5推進整體功能的實現。
PHP 測試工具與實踐
我所使用的 Yii Framework 支持單元測試和功能測試,這裡會用到 PHPUnit 和 Selenium Remote Control 這兩個測試工具。
PHPUnit 很多人都在用,通過很多斷言語句(比如 assertTrue, assertEquals)來檢查驗證目標代碼的行為。例如下面這個例子,用於測試博客評論功能。
// CDbTestCase 由 Yii 框架繼承自 PHPUnit 中的 PHPUnit_Framework_TestCase class CommentTest extends CDbTestCase { public function testApprove() { // 插入一條新的評論 $comment=new Comment; $comment->setAttributes(array( 'content'=>'comment 1', 'status'=>Comment::STATUS_PENDING, 'createTime'=>time(), 'author'=>'me', 'email'=>'[email protected]', 'postId'=>$this->posts['sample1']['id'], ),false); $this->assertTrue($comment->save(false)); // 驗證評論處於 pending 狀態 $comment=Comment::model()->findByPk($comment->id); $this->assertTrue($comment instanceof Comment); $this->assertEquals(Comment::STATUS_PENDING,$comment->status); // 調用 approve() 方法後,再驗證評論狀態為“已通過” $comment->approve(); $this->assertEquals(Comment::STATUS_APPROVED,$comment->status); $comment=Comment::model()->findByPk($comment->id); $this->assertEquals(Comment::STATUS_APPROVED,$comment->status); } ...... } |
Selenium 這個工具的介紹看上去感覺很強大,可以通過模擬瀏覽器訪問 Web 頁面的方式,來進行 UI 測試。用法和上面類似,
// WebTestCase 由 Yii 框架繼承自 PHPUnit_Extensions_SeleniumTestCase class PostTest extends WebTestCase { public function testShow() { $this->open('post/1'); // 驗證測試頁面文章標題 $this->assertTextPresent($this->posts['sample1']['title']); // 驗證評論表單 $this->assertTextPresent('Leave a Comment'); } ...... } |
我的觀點
TDD 並非教條。敏捷專家或是各種諮詢師在宣揚敏捷,很多團隊卻被整得很苦逼,就是因為他們像宗教一樣地信奉教條而不知變通。比如,TDD 就等同於 Unit Test 嗎?我們真的一定要在寫代碼前就寫好測試嗎?這些問題都是值得商榷的,也是爭論很多的。陳皓曾經談到 TDD 的幾個弊端——
- 1)TDD 可能會讓程序員敷衍了事,以為 test case 沒有錯就正確了。
- 2)TDD 可能會讓你忽略了軟件設計和架構以及程序的擴展性和重用性。
令人沮喪的是,現在說這些有點紙上談兵,因為我看到很多 PHP 程序員壓根就沒有寫測試,自己手動查看頁面沒問題後就上線了,然後陷入無盡的 bug report 循環。這屬於 各種流行的編程風格 中的撞大運編程。我覺得程序員編寫單元測試還是很有必要的,雖然會增加工作量,但以後的代碼維護會輕鬆很多。在 StackOverflow 上有人問 單元測試要測到什麼程度,“老闆為我的代碼付報酬,而不是測試”。最值得借鑒的答案則強調並非面面俱到,只是特別注意邊界情況,那些會讓團隊容易出錯的代碼。
眾所周知,Facebook 沒有專職的測試工程師,這是他們在質量控制中引以為豪並備受矚目的一點。所有的測試都是程序員自己完成,對自己的代碼質量負責,也就是 Eat your own dog food。每個程序員都是多面手,從最開始的一個 idea,到自己分析需求、架構,開發原型,自我管理版本控制,自己去測自己寫的代碼,自己去運維去部署。這樣才會對整個開發的過程有深刻的認識,親身體會某個環節的痛苦才會有想要改進的動力。
總結
每一個 PHP 程序員上輩子都是折翼的前端、美工、系統管理員和測試工程師……
這篇文章由lovelucy於2012-12-09 00:57發表在編程。你可以訂閱RSS 2.0 也可以發表評論或引用到你的網站。除特殊說明外文章均為本人原創,並遵從署名-非商業性使用-相同方式共享創作協議,轉載或使用請註明作者和來源,尊重知識分享。 |
批評不自由
則讚美無意義
今天早上突然看不了,怎麼辦啊!