測試也要設計—phpunit實踐

概述
本文闡述如何利用面向對象的思想,在phpunit框架下實現測試用例、數據文件、配置信息和lib庫等信息分離,並能有效組合。
也許有些QA認爲,測試代碼只要能知足測試要求便可,根本不須要有什麼設計的理念。其實否則,好的測試代碼,應該是可讀性強,可擴展性強。如下分享一個我在實際項目中的小想法來闡述這個觀點,僅做拋磚引玉之用。
具體實現
在autoFunc測試目錄下,建立conf、data、lib三個目錄,分別用於保存配置信息、數據文件和lib庫,測試用例直接放在autoFunc下。
 php


A 方案
直接在test_object_put_get.php中require config.php和util.php,以下:
require_once 'conf/config.php';
require_once 'lib/util.php';
彷佛很簡單,但這裏卻有兩個問題,phpunit的測試用例,是以class繼承PHPUnit_Framework_Testcase類的方式來組織的,這樣在testcase中就沒法訪問config.php的變量列表,或許你會說,這個很好解決呀,直接的testcase的class中指定config.php的變量名爲global類型便可,以下:
 html



弊端1:config.php中有幾個配置,testcase中就須要global幾個,難免太手工了。
弊端2:util.php中function沒法經過global方式聲明,在testcase中也就沒法訪問到。
B 方案
鑑於A 方案的侷限性,我提出了面向對象的方式,來實現配置信息、測試用例、lib庫有效的隔離。B 方案最突出的地方在於引入了繼承的概念,容我一一道來。
config.php
測試用例中總有一些常量會被反覆使用到,使用配置文件的方式是全部測試人員的共識,在這裏,我作了一個小小改善,將config信息包裝爲類的方式,以便於在lib庫的類中使用到,以下:
class Config{
public $WEB_SITE = "http://test.com:8090/";
public $BUCKET = "vincent";
public $ACCESS_KEY = "jhPLaYVh11wo";
public $SECURE_KEY = "A23mtEjHwv1z";
public $SIGN_FLAG="TST";
public $DATA_DIR = "./data/";

//no use now
public $IP = "";
public $TIME = "";
}
util.php
編寫測試用例時,總會有一些邏輯處理片斷反覆的出現,這形成了測試代碼的大量冗餘,也加大了維護成功,將這些邏輯處理封裝爲一個個函數是第一個步,以後將通用的函數抽取爲lib庫的形式,而那些函數適合抽取爲lib庫。根據經驗我列舉幾個lib的共有特性:
1.與測試用例邏輯無關
2.完成單一職責
3.可被其餘用例共用
若是知足這三個條件,那這個函數就能夠抽取爲lib庫,以下文的signature簽名函數。
在實際項目中,我抽取了部分函數爲lib庫,並將lib庫也封裝爲類的形式,同時繼承於class Config,以下:
require_once 'conf/config.php';
class Util extends Config{
/**
* 返回簽名串
* @param string $method
* @param string $object
*/
public function signature($method, $object){
$content = "$this->SIGN_FLAG\nMethod=$method\nBucket=$this->BUCKET\nObject=$object\n";
if($this->IP != ""){
$content .= "Ip=".$this->IP."\n";
}
if($this->TIME != ""){
$content .= "Time=".$this->TIME."\n";
}
$sign = "?Sign=$this->SIGN_FLAG:$this->ACCESS_KEY";
return $sign;
}
}
注意:這裏Util類繼承於Config類,也就繼承了Config類中的全部成員變量,故在Util類中能夠直接經過$this指針直接訪問到配置信息。
TEST CASES
testcases中經過在setUp()函數中new一個Util對象,這樣就能夠輕鬆使用lib庫中全部方法了,以下:
require_once 'PHPUnit/Framework.php';
require_once 'lib/util.php';框架

class ObjectPutGET extends PHPUnit_Framework_Testcase{
protected $util;
protected function setUp(){
$this->util = new Util();
}
public function testNormal(){
$object = '/normalObj';
$sign = $this->util->signature("PUT", $object);
$url = $this->util->WEB_SITE.$this->util->BUCKET.$object.$sign;
$http = curl_init();
$infile = fopen("data/file1", "r");
curl_setopt($http, CURLOPT_URL, $url);
curl_setopt($http, CURLOPT_INFILE, $infile);
curl_setopt($http, CURLOPT_INFILESIZE, 8);
curl_setopt($http, CURLOPT_UPLOAD, 1);
curl_exec($http);
curl_close($http);
fclose($infile);
}
}
這裏testcases中有一個protect的成員變量$util,並在setUp()中初始化,這樣在每一個testcase中均可以使用$this->util來訪問Util類中的全部方法和變量了。
問題:測試中可能遇到這樣的問題,lib庫的function依賴於config中的配置,在測試用例中調用function時,又但願能用不一樣的參數。
解決:按照gtest的測試經驗,須要爲function提供額外的參數,供傳入不一樣的值。既然使用面向對象了,這裏就簡單了,只須要經過實例化的lib庫調用$this->util->配置項,直接更改配置項信息。若是但願封裝好點,能夠設置get、set方法分別用於配置項的get和set。
數據驅動
將測試數據保存到data/目錄下的相應文件中,經過php unit的dataprovider機制與測試代碼結合,將測試數據與用例邏輯解耦合,增長case只須要相應增減數據文件,不須要變動用例邏輯,下降維護成本,提升可擴展性。
/**
* dataprovider for testObjectFileType
*/
public function fileType(){
return array(
array("fputtype.txt", "/putfiletype.txt"),
array("fputtype.docx", "/putfiletype.docx"),
array("fputtype.pdf", "/putfiletype.pdf"),
array("fputtype.xls", "/putfiletype.xls"),
array("fputtype.mp3", "/putfiletype.mp3"),
array("fputtype.mkv", "/putfiletype.mkv"),
array("fputtype.rar", "/putfiletype.rar")
);
}
/**
* 文件,文件類型爲txt word excel pdf mp3 mkv rar
* @dataProvider fileType
*/
public function testObjectFileType($fileType, $object_name){
$fileName = $this->util->DATA_DIR.$fileType;
$obj = $object_name;
//put object
$result = $this->util->putObject($fileName, $obj);
$this->assertEquals("", $result);
//get object
$result = $this->util->getObject($obj);
//check
$expect = md5(file_get_contents($fileName));
$actual = md5($result);
$header = new Header(file_get_contents($this->util->HEADER_FILE));
$etag = $header->getETag();
$this->assertEquals($expect, $actual);
$this->assertEquals($expect, $etag);
}
這樣組織後,測試用例、配置信息、數據文件以及lib庫就解耦了,無論修改哪部分,均可以直接找到並修改,不用擔憂會對其餘case形成什麼影響。
A. 編寫測試用例,在測試用例根目錄下找到對應測試文件,增減相應的case邏輯便可,而且能夠在測試用例中輕鬆調用lib庫,動態修改配置信息。
B. 修改數據文件?兩步便可,在data目錄下增減數據文件,修改對應測試用例的數據驅動信息。
C. 在conf目錄中修改配置信息,因爲配置信息是全局的,修改已有配置信息須要慎重。
D. lib庫與conf同樣是全局可見的,修改已有function須要考量對其餘case有沒有影響。
總結
不只僅RD的代碼須要可擴展性,QA的測試代碼一樣也須要。
測試也須要設計。curl

(做者:zhouxiuhu)ide

 

相關文章
相關標籤/搜索