在Mac OS上搭建PHP的Yii架構及相關測試環境,osyii
YII整合了單元測試和功能測試,藉助phpunit和selenium實現。筆者在配置過程中遇到了不少麻煩,紀錄在此。
必要概念
selenium
selenium是個著名的自動化測試載入器,可以調起本地的瀏覽器來完成測試,所以可以用來自動化測試web項目。selenium分為服務端和用戶端,服務端使用java開發,所以需要一個jdk,服務端在啟動時,會啟動一個http服務,用戶端通過與服務端進行http通訊,向服務端發起測試請求,服務端會自動調起瀏覽器完成測試。測試人員負責編寫用戶端指令碼,支援大部分主流的程式設計語言,當然實際上這是由於開源社區強大的威力,為不同的語言開發了針對selenium的介面程式而已,服務端和用戶端之間的協議筆者並沒有研究,因為這並不重要。
phpunit
phpunit是php語言的測試架構和工具,在進行單元測試的時候是使用它的架構,在進行功能測試的時候是使用它的工具。基於這個測試架構,有人在此基礎上做了selenium的php介面程式,作為phpunit的擴充存在。
YII架構如何整合
Yii在phpunit的基礎上,為測試做了一些簡單的封裝。因此,使用Yii來進行測試的時候,需要依賴上述兩者。
環境安裝
Firefox
selenium-server能夠識別的瀏覽器並不多,似乎是IE和Firefox,所以在OSX上先安裝好Firefox瀏覽器。安裝瀏覽器跟一般的軟體安裝沒有大的區別,這裡不累述了。
JDK
由於selenium-server是使用java開發的,我們需要先安裝好JDK,百度搜尋JDK下載安裝即可。不再累述。
selenium-server
首先來安裝selenium的server版本。在osx下,可以使用brew來安裝,比較方便:
$ brew install selenium-server-standalone
由於selenium-server的源在googleapis上,所以需要翻牆才能進行操作,事實上,如果不翻牆,其他步驟也比較困難。
安裝完成後的提示:
To have launchd start selenium-server-standalone at login: ln -sfv /usr/local/opt/selenium-server-standalone/*.plist ~/Library/LaunchAgentsThen to load selenium-server-standalone now: launchctl load ~/Library/LaunchAgents/homebrew.mxcl.selenium-server-standalone.plistOr, if you don't want/need launchctl, you can just run: selenium-server -p 4444
這裡明確告訴我們通過如下命令來啟動服務端
$ selenium-server -p 4444
正如所見,通常selenium-server偵聽4444連接埠,如果希望修改連接埠,那麼相應的Yii處需要修改一下配置。
phpunit
彎路
個人理解,phpunit是一個工具和架構的集合,工具歸工具,架構歸架構。從官網的文檔看,phpunit的工具部分,是以phar包的形式發布的,而架構部分是通過pear管理的。那麼先來記錄一下這兩個概念。沒有興趣的可以跳過這節。
phar是一種php打包方案。也就是可以把一個php程式或者php網站打包在一起分發,甚至被作為一個功能模組調用。因此,phpunit完全可以將工具程式打包成phar,執行phar的時候,通常需要使用php命令。
$ wget https://phar.phpunit.de/phpunit.phar$ chmod +x phpunit.phar$ sudo mv phpunit.phar /usr/local/bin/phpunit$ phpunit --versionPHPUnit x.y.z by Sebastian Bergmann and contributors.
用上面的命令可以下載phpunit的可執行檔,可以看到這是個phar包
pear是php擴充庫的體系,因為早期php複用比較困難。編譯型語言由於文法比較緊湊和嚴謹,比較容易複用。而php由於靈活多變,複用起來學習成本比較高,於是pear就提出了一個編程規範和分發體系來實現php的功能複用,現在似乎pear已經被composer替代了(下面會說)。不過古老的東西既然已經走過彎路了不妨記下來。
在mac下可以這麼安裝pear:
$ wget http://pear.php.net/go-pear.phar$ sudo php -d detect_unicode=0 go-pear.phar
可以看到,go-pear也是個phar,只不過它是一個安裝pear的php指令碼,使用php命令可以執行。安裝過程中會提示是否要修改php.ini檔案:
WARNING! The include_path defined in the currently used php.ini does notcontain the PEAR PHP directory you just specified:If the specified directory is also not in the include_path used byyour scripts, you will have problems getting any PEAR packages working.Would you like to alter php.ini ? [Y/n] : Yphp.ini include_path updated.Current include path : .:Configured directory : /usr/share/pearCurrently used php.ini (guess) : /etc/php.iniPress Enter to continue: The 'pear' command is now at your service at /usr/bin/pear** The 'pear' command is not currently in your PATH, so you need to** use '/usr/bin/pear' until you have added** '/usr/bin' to your PATH environment variable.
從這段提示我們可以得知:
pear的可執行程式安裝在/usr/bin/pear
pear有個工作目錄是/usr/share/pear,這個工作目錄需要添加到php.ini中,如果讓安裝程式自動添加的話,將是這樣的:
;***** Added by go-pearinclude_path=".:/usr/share/pear";*****
當我們在php使用require等包含其他檔案的函數時,php其實除了搜尋目前的目錄,還會搜尋include_path。這樣配置就表明,通過pear安裝的程式碼將存放在工作目錄,而且php能夠找到,預設在工作目錄下會有一個System.php,所以以下代碼是可以工作的:
<?php require 'System.php';?>
使用composer安裝
本來,phpunit可以通過pear來安裝的,然而,時過境遷,在composer大行其道的時代,phpunit也宣布全面支援composer,並且放棄pear,原本通過pear的安裝方式果然都不行了。最後逼不得已,只能上composer(話說包管理工具真是多的十個手指不夠用了,將來有機會來個橫向比較)。
首先安裝composer,在翻牆狀態下:
$ brew update$ brew tap josegonzalez/homebrew-php$ brew tap homebrew/versions$ brew install php55-intl$ brew install josegonzalez/php/composer
這樣composer就裝好了。
在項目的根目錄下,建立一個composer.json,寫入:
{ "require-dev": { "phpunit/phpunit": "4.7.*", "phpunit/php-invoker": "*", "phpunit/dbunit": ">=1.2", "phpunit/phpunit-selenium": ">=1.2", "phpunit/phpunit-story": "*" }}
上面的phpunit-selenium就是基於phpunit寫的selenium用戶端庫,詳見文後的參考資料。
然後在項目根目錄下,執行
$ sudo composer install
composer會根據這個composer.json檔案在根目錄建立一個vendor目錄,並將依賴的東西全部下載到這個目錄中,其中vendor/bin下面有phpunit的可執行檔。
由於是Yii的項目,所以cd到/protected/tests目錄下,執行如下命令即可啟動預設的SiteTest.php裡面的測試方法: (注意在執行前,保持selenium-server開啟狀態)
$ ../../vendor/bin/phpunit functional/SiteTest.php
會看到firefox會在執行過程中自動啟動,並由如下日誌輸出:
PHPUnit 4.7.7 by Sebastian Bergmann and contributors.Warning: Deprecated configuration setting "selenium" used.Time: 11.52 seconds, Memory: 6.50MbOK (1 test, 1 assertion)
phpunit工具程式會自動找到tests/phpunit.xml這個設定檔並根據此來進行某些配置,而Yii會利用phpunit和phpunit-selenium的架構來與selenium-server端通訊,server端會啟動瀏覽器,並將日誌和結果等返回給用戶端。整個過程大致就是這樣的。
測試
測試是軟體開發中必不可少的環節.無論我們是否意識到,在開發Web應用的時候,我們始終都是在測試的.例如, 當我們用PHP寫了一個類時, 我們可能會用到一些注入 echo 或者 die 語句來顯示我們是否正確地實現了某個方法;當我們實現了包含一套複雜的HTML表單的web頁面時, 我們可能會試著輸入一些測試資料來確認頁面是否是按照我們的預期來互動的.更進階的開發人員則會寫一些代碼來自動完成這個測試過程, 這樣一來每當我們需要測試一些東西的時候, 我們只需要調用代碼, 剩下來的就交給電腦了. 這就是所謂的 自動化的測試, 也是本章的主要話題.
Yii 提供的測試支援包括 單元測試 和 功能測試.
單元測試檢驗了代碼的一個獨立單元是否按照預期工作. 在物件導向編程中, 最基本的代碼單元就是類. 因此, 單元測試的主要職責就是校正這個類所實現的每個方法工作都是正常的. 單元測試通常是由開發了這個類的人來編寫.
功能測試檢驗了特性是否按照預期工作(如:在一個部落格系統裡的提交操作).與單元測試相比, 功能測試通常要進階一些, 因為待測試的特性常常牽涉到多個類. 功能測試通常是由非常瞭解系統需求的人編寫.(這個人既可以是開發人員也可以是品質工程師).
測試驅動開發
以下展示的便是所謂的 測試驅動開發 (TDD) 的開發週期:
- 建立一個涵蓋要實現的特性的新的測試. 測試預計將在第一次執行的時候失敗, 因為特性尚未實現.
- 執行所有測試,確保這個新的測試是失敗的.
- 編寫代碼來使得測試通過.
- 執行所有測試,確保所有測試通過.
- 重構新編寫的代碼並確保這些測試仍然能夠通過.
- 重複步驟1至5推進整體功能的實現.
構建測試環境
Yii 提供的測試支援需要 PHPUnit 3.5+ 和 Selenium Remote Control 1.0+.請參照他們提供的文檔來安裝 PHPUnit 和 Selenium Remote Control.
當我們使用 yiic webapp 控制台命令來建立一個新的 Yii 應用時, 它將會產生以下檔案和目錄供我們來編寫和完成測試.
testdrive/
protected/ 包含了受保護的應用檔案
tests/ 包含了應用測試
fixtures/ 包含了資料 fixtures
functional/ 包含了功能測試
unit/ 包含了單元測試
report/ 包含了 coverage 報告
bootstrap.php 這個指令碼在一開始執行
phpunit.xml PHPUnit 設定檔
WebTestCase.php 基於 Web 的功能測試基類
如上所示的, 我們的測試代碼主要放在 fixtures, functional 和 unit 這三個目錄下, report 目錄則用於儲存產生的程式碼 coverage 報告.
我們可以在控制台視窗執行以下命令來執行測試(無論是單元測試還是功能測試):
% cd testdrive/protected/tests% phpunit functional/PostTest.php // 執行單個測試% phpunit --verbose functional // 執行 'functional' 下的所有測試% phpunit --coverage-html ./report unit
上面的最後一條命令將執行 unit 目錄下的所有測試然後在 report 目錄下產生出一份 code-coverage 報告. 注意要產生 code-coverage 報告必須安裝並開啟PHP的 xdebug 擴充 .
測試的引導指令碼
讓我們來看看 bootstrap.php 檔案裡會有些什麼. 首先這個檔案有點特殊,因為它看起來很像是 入口指令碼, 而它也正是我們執行一系列測試的入口.
$yiit='path/to/yii/framework/yiit.php';$config=dirname(__FILE__).'/../config/test.php';require_once($yiit);require_once(dirname(__FILE__).'/WebTestCase.php');Yii::createWebApplication($config);
如上所示, 首先我們包含了來自 Yii 架構的 yiit.php 檔案, 它初始化了一些全域常量以及必要的測試基類.然後我們使用 test.php 這個設定檔來建立一個應用執行個體.如果你查看 test.php 檔案, 你會發現它是繼承自 main.php 這個設定檔的, 只不過它多加了一個類名為 [CDbFixtureManager] 的 fixture 應用組件.
return CMap::mergeArray( require(dirname(__FILE__).'/main.php'), array( 'components'=>array( 'fixture'=>array( 'class'=>'system.test.CDbFixtureManager', ), /* 去除以下注釋可為測試提供一個資料庫連接. 'db'=>array( 'connectionString'=>'DSN for test database', ), */ ), ));
當我執行那些涉及到資料庫操作的測試時, 我們應該提供一個測試專用的資料庫以便測試執行不會干擾到正常的開發或者生產活動. 這樣一來, 我們紙需要去除上面 db 配置的注釋, 然後填寫 connectionString 屬性的用以串連到資料庫的DSN(資料來源名稱)即可.
通過這樣一個啟動指令碼, 當我們執行單元測試時, 我們便可以獲得一個與服務需求類似的應用執行個體, 而主要的不同就是測試擁有一個 fixture 管理器以及它專屬的測試資料庫.
定義特定狀態(Fixtures)
自動化的測試需要被執行很多次.為了確保測試過程是可以重複的, 我們很想要在一些可知的狀態下進行測試, 這個狀態我們稱之為 特定狀態. 舉個例子,在一個部落格應用中測試文章建立特性, 每次當我們進行測試時, 與文章相關的表(例如. Post 表 , Comment 表)應該被恢複到一個特定的狀態下. PHPUnit 文檔 已經很好的描述了一般的特定狀態的構建. 而本節主要介紹怎樣像剛才描述的例子那樣構建資料庫特定狀態.
設定構建資料庫的特定狀態,這恐怕是測試以資料庫為後端支援的應用最耗時的部分之一.Yii 引進的 [CBbFixtureManager] 應用組件可以有效減輕這一問題.當進行一組測試的時候,它基本上會做以下這些事情:
在所有測試回合之前,它重設測試相關資料為可知的狀態.
在單個測試回合之前, 它將特定的表重設為可知狀態.
在一個測試方法執行過程中, 它提供了供給特定狀態的行資料的提供者.
請按如下使用我們在 應用配置 中配置的 [CDbFixtureManager].
return array( 'components'=>array( 'fixture'=>array( 'class'=>'system.test.CDbFixtureManager', ), ),);
然後我們在目錄 protected/tests/fixtures下提供一個特定狀態資料. 這個目錄可以通過配置應用設定檔中的 [CDbFixtureManager::basePath] 屬性指定為其他目錄.特定狀態資料是由多個稱之為特定狀態檔案的PHP檔案組合而成.每個特定狀態檔案返回一個數組, 代表資料的一個特定表的初始行.檔案名稱和表名相同.以下則是將 Post 表的特定狀態資料存放區於名為 Post.php 檔案裡的例子.
<?phpreturn array( 'sample1'=>array( 'title'=>'test post 1', 'content'=>'test post content 1', 'createTime'=>1230952187, 'authorId'=>1, ), 'sample2'=>array( 'title'=>'test post 2', 'content'=>'test post content 2', 'createTime'=>1230952287, 'authorId'=>1, ),);
正如我們所見, 上面返回了兩行資料. 每一行都表示一個數組,其鍵是表的欄位名,其值則是對應的欄位值.每行的索引都是稱之為行別名的字串(例如: simple1, simple2). 稍後當我們編寫測試指令碼的時候, 我們可以方便地通過它的別名調用這行資料.你也許注意到了我們並未在上述特定狀態中指定 id 欄位的值. 這是因為 id 欄位已經被定義為自增主鍵了,它的值也會在我們插入新資料的時候自動產生.
當 [CDbFixtureManager] 第一次被引用時, 它會仔細檢查所有的特定狀態檔案然後使用他們重設對應的表.它通過清空表,重設表主鍵的自增序列值,然後插入來自特定狀態檔案的資料行到表中來重設表.
有時候,我們可能不想在一套測試前重設特定狀態檔案裡描述的每一個表, 因為重設太多的特定狀態檔案可能需要很多時間.這種情況下,我們可以寫一個PHP指令碼來定製這個初始化過程.這個指令碼應該被儲存在存放特定狀態檔案的目錄下,並命名為 init.php.當 [CDbFixtureManager] 檢測到了這個指令碼的存在, 它將執行這個指令碼而不是重設每一個表.
不喜歡使用預設來重設表也是可以的,例如: 清空表然後插入特定狀態資料. 如果是這種情況, 我們可以為指定的特定狀態檔案編寫一個初始化指令碼.這個指令碼必須名稱為表名+.init.php. 例如: Post 表的初始化指令檔就是 Post.init.php. 當 [CDbFixtureManager] 發現了這個指令碼,它將執行這個指令碼而不是採用預設的方式去重設該表.
Tip: 太多的特定狀態檔案大大延長了測試時間.因此, 你應該只為那些在測試中資料會發生變化的表提供特定狀態檔案. 那些做為尋找服務的表不會改變,因此不需要特定狀態檔案.
您可能感興趣的文章:
- win7安裝php架構Yii的方法
- Nginx配置PHP的Yii與CakePHP架構的rewrite規則樣本
- 詳解PHP的Yii架構中日誌的相關配置及使用
- 執行個體講解yii2.0在php命令列中啟動並執行步驟
- Yii 串連、修改 MySQL 資料庫及phpunit 測試連接
- PHP YII架構開發小技巧之模型(models)中rules自訂驗證規則
- PHP Yii架構之表單驗證規則大全
- PHP基於yii架構實現產生ICO表徵圖
- 列舉PHP的Yii 2架構的開發優勢
http://www.bkjia.com/PHPjc/1099051.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/1099051.htmlTechArticle在Mac OS上搭建PHP的Yii架構及相關測試環境,osyii YII整合了單元測試和功能測試,藉助phpunit和selenium實現。筆者在配置過程中遇到了不少麻煩...