測試也要設計—phpunit實踐
概述
本文闡述如何利用物件導向的思想,在phpunit框架下實現測試用例、資料檔案、配置資訊和lib庫等資訊分離,並能有效組合。
也許有些QA認為,測試程式碼只要能滿足測試要求即可,根本不需要有什麼設計的理念。其實不然,好的測試程式碼,應該是可讀性強,可擴充套件性強。以下分享一個我在實際專案中的小想法來闡述這個觀點,僅作拋磚引玉之用。
具體實現
在autoFunc測試目錄下,建立conf、data、lib三個目錄,分別用於儲存配置資訊、資料檔案和lib庫,測試用例直接放在autoFunc下。
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型別即可,如下:
弊端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
Method=$method
Bucket=$this->BUCKET
Object=$object
“;
if($this->IP != “”){
$content .= “Ip=”.$this->IP.”
“;
}
if($this->TIME != “”){
$content .= “Time=”.$this->TIME.”
“;
}
$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的測試程式碼同樣也需要。
測試也需要設計。
(作者:zhouxiuhu)
相關文章
- phpunit測試成功phpunit測試實踐程式碼PHP
- 實踐 Laravel phpunitLaravelPHP
- phpunit單元測試PHP
- Laravel 測試: PHPUnit 入門教程LaravelPHP
- PHP單元測試框架PHPUnit的使用PHP框架
- 效能測試必知必會:Shell指令碼設計實踐指南指令碼
- 來 ! 玩玩PHPUnit的資料庫測試 (上)PHP資料庫
- Vodafone A/B測試實踐
- 精準測試實踐
- Locust效能測試實踐
- vivo 基於 JaCoCo 的測試覆蓋率設計與實踐
- 網路程式設計在自動化測試中的實踐(十五)程式設計
- symfony2 用phpunit進行單元測試PHP
- 介面自動化測試系列之PHPUnit-GET請求介面測試方法PHP
- Go 單元測試實踐Go
- HTTP介面測試實踐(一)HTTP
- 契約測試Pact實踐
- ChaosBlade混沌測試實踐
- 測試用例最佳實踐
- [Composer 包分享] codedungeon/phpunit-result-printer 優雅的展示 PHPUnit 測試結果PHP
- Docker與自動化測試及其測試實踐Docker
- 介面測試框架接入效能測試實踐分享框架
- 程式設計實踐考試的入門模板程式設計
- Steam測試中國版,單機遊戲也要防沉迷遊戲
- Parasoft軟體測試實踐:什麼是左移測試?
- Android 單元測試實踐Android
- Golang專案的測試實踐Golang
- 前端測試套件構建實踐前端套件
- API自動化測試實踐API
- DevOps 中的測試實踐dev
- DevOps中的測試實踐dev
- 介面測試工具 Postman 使用實踐Postman
- 異常測試實踐與梳理
- 敏捷測試的方法與實踐敏捷測試
- SOA 非功能測試最佳實踐
- 故障測試 Byteman 上手實踐
- 程式設計師也要養生程式設計師
- 基於AI的移動端自動化測試框架的設計與實踐AI框架