|
在開發(fā)具有一定復雜程度的應用程序的過程中,會遇到bug,邏輯錯誤和合作等難題,這些問題處理得很好就會成功地開發(fā)應用程序,否則就會得到延期、超出預算的應用程序,以及雇員流失問題。 這些問題是不可預防的,但是有一系列的工具可以幫助你更好地管理項目,并實時跟蹤項目的進展。這些工具組合在一起會形成一種被稱為持續(xù)集成(continuous integration)的編程技術。 任何持續(xù)集成項目都包含四個主要的組件:版本控制、單元測試、部署和調試。通常,在使用PHP語言的項目中,這四個領域所用的工具分別為Subversion、PHPUnit、Phing 和Xdebug。為了將這些工具全部連接在一起,可以使用持續(xù)集成服務器Xinc。 1 用作版本控制的Subversion Subversion是一個版本控制系統(tǒng)(通??s寫為SVN),可以用來跟蹤對應用程序文件作出的修改。如果是PHP開發(fā)人員,應該很熟悉版本控制了。你用過的可能是并行版本系統(tǒng)(CVS),這是Subversion之前的系統(tǒng),并且仍然被廣泛使用。 在兩個或者多個開發(fā)人員修改同一個文件的情況下,Subversion有助于預防一種常見的情形的發(fā)生。如果不用版本控制系統(tǒng),開發(fā)人員需要下載源文件(通常是從FTP服務器上),做出修改,然后再上傳文件,覆蓋原來的文件。如果另外一個開發(fā)人員同時下載了相同的源文件,也做了一些修改,然后上傳文件,就會覆蓋之前的那個開發(fā)人員的工作。 使用Subverion,這一情形不會再發(fā)生。不用下載文件,開發(fā)人員需要簽出(check out)文件的當前版本,做出修改,然后提交(commit)這些修改。 在提交過程中,Subverion會檢查是否有其它用戶在這個文件被下載之后對文件作出修改。如果文件已經被修改,Subversion會試圖合并所有的修改,以便使最終的文件包含所有的修改內容。如果這些修改不會影響到文件的同一部分,這樣處理沒有任何問題。不過,如果修改了相同代碼,沖突就會發(fā)生。最后的提交者要負責將他的修改內容與之前的修改內容集成起來。使用這一方法不會丟失任何工作,并且項目會保持內部一致。 8.1.1 安裝Subversion 在幾乎所有的Linux發(fā)行版中,Subversion都可以通過包管理程序安裝。如果使用Debian/Ubuntu風格的包管理程序,需要輸入以下命令安裝Subversion。 > apt-get install subversion subversion-tools 這會為你提供創(chuàng)建本地Subversion存儲庫所需要的所有工具。存儲庫(repository)是一個受到版本控制的文件和文件夾的目錄。通常,可以為多個項目創(chuàng)建多個存儲庫,這些工具允許你在服務器上管理它們。 說明 Subversion 在設計上可以通過Apache web服務器來遠程操作。要實現(xiàn)這一功能,需要另外安裝libapache2-svn 包,它提供了Apache和Subversion之間的綁定。然后,應該特別注意正確地設置服務器的安全性。如果選擇使用Apache和Subverion,推薦你部署安全套接字層(SSL)的客戶端證書,就像第 21章所介紹的那樣。 1.2 設置Subversion 管理Subversion存儲庫實際上非常簡單。首先,需要在服務器上找到一個合適的位置保存存儲庫。推薦放在/usr/local/svn中,不過其它地方是沒有問題的。下一步,使用svnadmin create 命令在這個目錄中創(chuàng)建一個存儲庫。 > svnadmin create myfirstrepo 現(xiàn)在會看到一個新的目錄( /usr/local/svn/myfirstrepo ),它包含了管理項目所需要的所有文件和數(shù)據(jù)庫。 下一步是獲得存儲庫的一個工作簽出副本。簽出副本( checkout ) 是Subversion的工作空間,可以在這里添加文件以及修改文件。不要在存儲庫目 錄中直接修改文件,這一點非常重要。為了創(chuàng)建簽出副本,需要轉到一個新的 目錄,并執(zhí)行svn checkout命令,建議使用home目錄。如下所示。 > cd ~ > svn checkout file:///usr/local/svn/myfirstrepo checked out revision 0. 注意 不要在包含存儲庫的目錄 /usr/local/svn 中執(zhí)行svn checkout 命令。 現(xiàn)在便可以看到存儲庫目錄了。如果有一個現(xiàn)存的項目,可以使用 svn import 命令將這些文件置于版本控制之下了,如下所示。 > svn import ~/existingproject file:///usr/local/svn/myfirstrepo 你將需要輸入一個提交信息。這些信息是很關鍵的,它可以保存修改的人、修改的文件以及修改的原因。對于初始導入過程來說,只需說明Initial Import of <Project> 即可,然后保存文件。 提示 通過設置EDITOR 環(huán)境變量,可以修改Subversion用來輸入提交信息的編輯器,例如,在Bash外殼環(huán)境中,可以使用export EDITOR=pico命令將編輯器修改為Pico。 這個項目現(xiàn)在已經置于版本控制之下了,但是導入之前創(chuàng)建的簽出副本現(xiàn)在已經過期了,它并沒有反映導入的內容。這是故意設計成這樣的,所有的簽出副本必須使用svn update命令來手工更新。 > svn update A index.html updted to version 1. 提示 在修改文件之前,經常更新Subversion 會減少需要處理的合并的次數(shù)。 現(xiàn)在,當你修改文件時,便是在簽出副本中修改它們。實際上,你可以備份原來的文件,因為不再需要對這些文件進行處理了。 你應該會注意到,在簽出副本的每個目錄下都有一個.svn目錄。在某些情況下,你可能希望獲取一個沒有這些目錄的副本,例如在創(chuàng)建應用程序的發(fā)行版本時。要獲得一個不包含工作目錄的項目的副本,可以使用svn export命令。 > svn export file:///usr/local/svn/myfirstrepo ~/exportdirectory A /home/user/exportdirectory A /home/user/exportdirectory/index.html Exported revision 1. 要向存儲庫添加新文件,你需要使用svn add命令,添加文件是本地的修改行為,這和導入文件不同,不會被保存到存儲庫中,除非使用svn commint命令(這個命令下面將會討論到)顯示地保存這一修改。 > echo test > newfile.txt > svn add newfile.txt A newfile.txt > svn commit Adding newfile.txt Transmitting file data . Committed revision 2. 1.3 提交修改和解決沖突 目前,文件已經處于版本控制之下了,你可以根據(jù)需要去修改它們,當要將修改保存到存儲庫時,需要提交它們。為了確定是否有需要提交的修改, 可以使用svn status 命令。 > echo changed > newfile.txt > svn status M newfile.txt 這一例子顯示出newfile.txt 文件已經被修改過了。在文件名旁邊的M字符表示所有本地的文件修改已經與存儲庫中的修改合并在一起了。因此,應該提交這些修改內容。 如果不想保留修改內容,可以使用svn revert 命令來恢復舊文件。 > svn revert newfile.txt Reverted 'newfile.txt' > cat newfile.txt test 下一步是模擬同一項目中另一個開發(fā)人員的情形,需在home目錄中創(chuàng)建第二個簽出副本。 > svn co file:///usr/local/svn/myfirstrepo ~/myfirstrepo2 A /home/user/myfirstrepo2/newfile.txt A /home/user/myfirstrepo2/indeex.html Checked out revisiion 2. 然后,在myfirstrepo2目錄下向newfile.txt文件添加一些內容。 > echo newdata >> newfile.txt > svn commit Sending newfile.txt Transmitting file data . Committed revision 3. 返回到myfirstrepo目錄,并且不要去更新它。打開newfile.txt文件,注意到另外一個簽出副本的修改還沒有反映到這個文件中?,F(xiàn)在,在這個簽出副本中對文件中的同一行做一個相似但不同的修改,并且嘗試去提交它。 會出現(xiàn)一個表示過期的錯誤提示,表明其他人已經修改了這個文件。你必須獲得文件的最后版本才能提交修改?,F(xiàn)在執(zhí)行更新操作會導致文件處于沖突狀態(tài),這是因為在兩個簽出副本中對同一行內容都做了修改。 > echo alternativedata >> newfile.txt > svn commit Sending newfile.txt svn: Commit failed (details follow): svn: Out of date: 'newfile.txt' in transaction '3-1' > svn udpate C newfile.txt Updated to revision 3. 請注意newfile.txt 旁邊的C字符。這一字符表明文件處于沖突狀態(tài)。 如果在這個目錄中運行l(wèi)s命令,將會看有三個新文件已經被創(chuàng)建。 > ls -l index.html newfile.txt newfile.txt.mine newfile.txt.r2 newfile.txt.r3 這些文件表示了沖突狀態(tài)。r2文件是原始的文件,r3文件包含了在 myfirstrepo2中所做的修改,.mine文件是本地的修改。.txt文件也已經被修 改,并且現(xiàn)在包含的修改內容,從而可以更容易地解決沖突。 > cat newfile.txt test <<<<<<<<< .mine alternativedata ========== newdata >>>>>>>>> .r3 現(xiàn)在需要newfile.txt文件包含所有的修改內容。首先將<<<、>>>文本行和===文本刪除,然后添加或者刪除對文件的修改內容,以便得到理想的最終結果。在更加復雜的合并情況中,如果遇到功能互相覆蓋的情況,可以拒絕特定的修改或者重新處理這些內容。不過,在這個例子中,希望同時保留newdata修改和本地修改。最終的文件應該是這樣寫的。 > cat newfile.txt test newdata alternativedata 下一步需要告訴Subversion已經解決了沖突,使用svn resolved 命令可以達到這一目的。 > svn resolved newfile.txt Resolved conflicted state of 'newfile.txt' 這一操作刪除了三個額外的文件。通過幫助解決沖突,這些文件已經完成了它們的全部功能,現(xiàn)在已經不再需要它們了。 最后一步是調用svn commit命令提交已經解決好的修改內容。 svn commit Sending newfile.txt Transmitting file data . Committed revision 4. 可以看到,使用這一過程,開發(fā)人員就不會輕易地覆蓋他們的代碼了。 1.4 激活對Subversion 的訪問功能 下一步是通過Apache激活對Subversion 的訪問功能。要實現(xiàn)這一目的,需要在Apache Web 服務器上創(chuàng)建一個虛擬主機。 創(chuàng)建好虛擬主機之后,只需要遵循以下格式向配置文件添加一個 <Location>標簽就可以了。 <Location /svn/myfirstrepo> DAV svn SVNPath /usr/local/svn/myfirstrepo </Location> 現(xiàn)在,可以從其他客戶端位置使用查找出自己的文件,而不是file:///usr/local/svn/myfirstepo ,因為這一路徑只適合在本地服務上的訪問。 注意 這里顯示<Location>標簽完全沒有考慮安全性因素。在顯示任何實際的代碼之前,請確保已正確地配置好驗證和SSL安全功能,關于SSL客戶端證書的安裝指南 2 用于單元測試的PHPUit PHPUnit 可以用來為應用程序創(chuàng)建單元測試。簡單來說,PHP的單元測試包括編寫專門用來測試其他PHP腳本的PHP腳本。這種類型的測試被稱為單元測試,這是因為測試裝置是用來測試單獨的代碼單元的,如類和方法,而且一次只測試一個單元。PHPUnit是用來編寫這些測試的一個優(yōu)秀的解決方案,并且遵循了面向對象的開發(fā)方法。 2.1 安裝PHPUnit 安裝PHPUnit是通過PEAR完成的,而且過程非常簡單。首先使用PEAR來“發(fā)現(xiàn)”pear.phpunit.de頻道。 > pear channel-discover pear.phpunit.de Adding Channel "pear.phpunit.de" succeeded Discovery of channel "pear.phpunit.de" succeeded 然后,安裝PHPUnit以及它所需要的附屬項。 > pear install --alldeps phpunit/PHPUnit downloading PHPUnit-3.1.9.tgz ... starting to download PHPUnit-3.1.9.tgz (116,945 bytes) ...................................done : 116,945 bytes install ok : channel://pear.phpunit.de/PHPUnit-3.1.9 根據(jù)系統(tǒng)上的PEAR布局,PHPUnit源文件應該可以在/usr/share/php/PHPUnit目錄中找到。 2.2 創(chuàng)建第一個單元測試 要開始創(chuàng)建單元測試,需要設置一個目錄結構。將測試放在哪里沒有一個特定的規(guī)范。一些開發(fā)人員將測試文件與被測試的代碼放在相同的目錄中。其他人創(chuàng)建一個單獨的測試目錄,并且復制了代碼目錄的結構,這樣有助于保持測試代碼的獨立性。在這個示例中使用了后一種方法。 首先,清除Subversion文件。 > svn rm index.html newfile.txt D index.html D newfile.txt > svn commit Deteing index.html Deteing newfile.txt Committed revision 5. 說明 在svn commit 命令下使用svn rm 命令會同時刪存儲庫和簽出 副本中的文件。標準的rm 命令不會從存 儲庫中刪除文件,文件將會在下一次使用svn update時恢復。 現(xiàn)在創(chuàng)建兩個目錄,一個用于保存代碼,另外一個用于保存測試。 > svn mkdir code tests A code A tests 在code 目錄中創(chuàng)建一個Demo類,這個類實現(xiàn)了一些很容易測試的操 作,即加法和減法,如代碼清單8-1所未。 代碼清單8-1 Demo類(./code/Demo.php) <?php class Demo { public function sum($a , $b){ return $a+$b; } public function subtract($a, $b){ return $a-$b; } } 下一步是創(chuàng)建如代碼清單8-2所示的單元測試。 代碼清單8-2 單元測試代碼(./test/Demotest.php) <?php require_once('PHPUnit/Frameword.php'); require_once('dirname(__FILE__).'/../code/Demo.php '); class DemoTest extends PHPUnit_Framwork_TestCase { public function testSum(){ $demo = new Demo(); $this->assertEquals(4,$demo->sum(2,2)); $this->assertNotEquals(3,$demo->sum(1,1)); } } ?> 現(xiàn)在,在tests目錄中,使用phpunit測試運行程序來運行測試。 > phpunit DemoTest PHPUnit 3.1.9 by Sebastian Bergmann. Time: 0 seconds OK (1 test) 可以看到,測試運行程序給出了測試已正確運行的信息。 2.3 理解PHPUnit 現(xiàn)在你已經了解了在哪里放置單元測試代碼以及如何從命令行調用它們,你可能會問PHPUnit_Framework_TestCase到底是什么,testSum()方法是如何工作的呢, PHPUnit測試通常遵循一個命名規(guī)范,即測試類的名稱就是被測試類的名稱加上單詞Test,并且這個類的文件名稱正是測試類的名稱加上.php擴展名。測試類名稱應該是組件的描述或者被測試的功能的描述。例如,測試用戶驗證功能(Authentication 類)的類應該命名為AuthenticationTest,并且被保存在AuthenticationTest.php中。 測試,更明確地說是測試用例,只是一個從PHPUnit_Framwork_TestCase繼承的類。這個測試類提供了訪問所有不同類型斷言的功能,并且負責運行針對目標類的所有測試方法。 在這個例子中,當給測試運行器傳入DemoTest類時,它會一次調用一個測試方法,并且同時收集信息,在每一個方法中,應該定義一套關于被測試代碼執(zhí)行功能的假設情況。這些假設需要被翻譯成斷言。如果希望sum()方法在處理2和2這兩個參數(shù)時總是返回4,那么應該編寫一個函數(shù)結果等于4的斷言。 不修改DemoTest類而是修改Demo類似便使得它的sum()方法不再返回有效結果,比如將+操作符改為-操作符就能達到這一目的。修改Demo類之后,當再次運行單元測試時,就會得到如下結果。 > phpunit DemoTest PHPUnit 3.1.9 by Sebastian Bergmann. Time: 0 seconds There was 1 failure: 1) testSum(DemoTest) Failed asserting that <integer:0> matches expected value <integer:4>. /home/user/myfirstrepo/tests/DemoTest.php:12 FAILURES1 Tests:1, Failures:1. 測試失敗的原因是sum()方法不再通過2+2=4的相等斷言。 你可以選擇使用幾十種斷言。在這個例子中,使用的是assertEquals斷言和assertNot-Equals斷言,這兩個斷言都是比較傳入的兩項是否相等的斷言。另外,還有用于對象的assertSame斷言、用于Boolean類型的assertFalse斷言或assertTure斷言,以及用于異常的setExpectedException斷言。要想獲得這些斷言的參考列表和準確的語法細節(jié),可參見網站上的PHPUnit手冊。 在編寫單元測試時,需要設置對象,就像在之前的測試中采用$demo = new Demao語句給對象賦值一樣。如果你總是為每個測試都做相同的設置,這種做法就顯得煩瑣。幸好,PHPUnit提供了兩個分別叫做setUp() 和tearDown()的方法,這兩個方法允許為每個測試定義公共的配置。代碼清單8-3顯示了相同的測試代碼,分開放置在setUp()方法和tearDown()方法中。
信息發(fā)布:廣州名易軟件有限公司 http://m.jetlc.com
|