自動化測試的主要任務是迴歸測試,所以不可能一個一個地去執行測試用例。要根據測試的目的來組織測試用例,規劃測試用例的架構。以方便測試用例的執行,測試用例的調試,出錯的時候問題定位以及測試用例的維護等。
5.1 自動化架構規劃
也許是個人原因吧,我比較喜歡測試數據和測試用例分開的規劃方式。這樣的規劃一是比較明確,二是如果出現了問題,我們定位問題的時候比較方便。三是如果被測對象有任何改動,我們只需要修改測試數據就可以了,不需要修改測試用例。
兼於以上優點,我們現在討論一下自動化架構的規劃。和Web自動化一樣,我們做如下規劃,在工程中創建以下文件夾:
(1)CommonClass:文件夾下存放所有的公共函數,常用的引用或是通用操作等等。
(2)Source:存放配置文件,圖片資源或是其他靜態資源。
(3)TestCases:存放具體的測試用例文件,一個測試用例文件最好是針對一個接口的所有測試用例,如果用例比較多可以分成多個測試文件。
(4)TestData:和測試用例文件相匹配的測試數據文件,以xml文件存方數據,數據的組織形式以測試用例爲單位對應該xml文件中的一個節點。
(5)TestSuites:根據不同的測試需要,把測試用例組織成不同的測試套件,然後在相應的環境中配置執行。
經過這樣的規劃,我們再寫自動化測試用例的時候,在相應的文件夾下添加對應的內容即可。
5.2 CommonClass文件夾內容講解
CommonClass中存放的是我們的公共函數,根據我們的接口自動化測試的需要,我們要創建以下幾個文件:
(1)CurlOperation.php 此文件的主要功能是實現對接口的調用,根據傳遞過來的參數,執行不同的代碼。代碼如下:
<?php
/**
* 具體的接口調用操作
*
* @author SXF
*/
class CurlOperation {
public static functionFetch($url, $get_params, $post_params=null) {
$str = '';
if(isset($get_params) && !empty($get_params)) {
$arr =array();
foreach ($get_paramsas $key => $value) {
array_push($arr, $key . '=' . urlencode($value));
}
$str =implode("&", $arr);
}
if (strpos($url,"?") !== FALSE) {
$url = $url .'&' . $str;
} else {
$url = $url .'?' . $str;
}
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch,CURLOPT_HEADER, 0);
curl_setopt($ch,CURLOPT_HTTPHEADER, array(
"User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US;rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 (.NET CLR 3.5.30729)",
"Accept-Language:en-us,en;q=0.5","DIVERSION-VERSION:1","SESSION-TOKEN:".TextOperation::readtoken()#封裝頭內容,讀取Session實現登錄狀態
));
if(isset($post_params)) {
// Post method
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_params);
//下面兩行是爲了https請求添加
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
}
$output =curl_exec($ch);
curl_close($ch);
return $output;
}
}
PHP常用的調用Post和Get接口的方法,爲了實現一些兒接口調用需要登錄,我們添加對HTTP頭的封裝,把Session的內容傳遞過來。
(2)DataOperations.php 存放相關的數據操作,我們在此處的主要作用就是讀取測試數據文件Xml,爲測試用例提供測試數據。代碼詳情如下:
<?php
/**
* 此類爲數據操作類
* @author SXF
* @date:2015-3-18
*/
classDataOperations {
var $doc;
public function __construct($filename) {
$this->doc=new DOMDocument();
$this->doc->load("TestData/".$filename);
}
public functionreadnodevalue($node1,$node2){
/*讀取Xml節點的值
* @param node1:數據根節點
* @param node2:數據節點
*/
foreach($this->doc->getElementsByTagName($node1) as $item)
{
$list =$item->getElementsByTagName( $node2 );
foreach ( $list as $list1 )
{
$value =$list1->nodeValue;
break;
}
}
return $value;
}
}
(3)ReadConfig.php 讀取Source下的配置文件config.xml,用於切換host,實現測試用例在不同的環境下運行。具體代碼如下:
<?php
/**
* 讀取配置文件中的數據
*@author SXF
*/
class ReadConfig {
var $doc;
public function __construct() {
$this->doc=new DOMDocument();
$this->doc->load("Source/Config.xml");
}
public function gethost($type)
{
//讀取配置文件中的Host信息
foreach($this->doc->getElementsByTagName("host") as$item)
{
$list =$item->getElementsByTagName( $type );
foreach ( $list as $list1 )
{
$value =$list1->nodeValue;
break;
}
}
return $value;
}
}
(4)TextOperation.php Text文件的讀寫函數,用於存放登錄接口返回的Token的值到Source/token.txt中,當其他的接口需要登錄狀態的時候,將Token讀取出來,封裝到Http頭中。當然也可以類似於log一樣,把錯誤的內容寫到Source/result.txt文件中,具體代碼如下:
<?php
/**
* Text文件相關操作
* @author lys
*/
classTextOperation {
public function writetxt($message){
$open=fopen("Source/result.txt","a+");
fwrite($open,$message."\r\n");
fclose($open);
}
public function writetoken($message){
$open=fopen("Source/token.txt","w+");
fwrite($open,$message."\r\n");
fclose($open);
}
public function readtoken(){
$open=fopen("Source/token.txt","r");
$token=fread($open,32);
fclose($open);
return $token;
}
}
(5)Require.php 爲了簡化代碼,我們將需要引用的文件全放到這個文件中,其他的測試用例文件再需要引用的時候,直接引用這個文件即可。具體代碼如下:
<?php
/*
*所有需要引用的文件
*/
require_once("CurlOperation.php");
require_once("DataOperations.php");
require_once("ReadConfig.php");
require_once("TextOperation.php");
5.3 Source文件夾內容講解
Source文件夾中存放的是配置文件,所要用到的圖片文件資源等,目前我的Source文件夾下有如下文件:
(1)img文件夾:存放的是測試用例要用到的圖片文件,裏面有十張圖片,以方便每次上傳的時候實現隨機調用。
(2)Config.xml配置文件。這是配置的是各個測試環境的調用host,這樣我們可以通過修改不同的參數來切換環境。文件內容如下:
<?xml version="1.0"encoding="UTF-8"?>
<!--
這裏是接口測試用到的相關配置
-->
<Setting>
<host>
<online>http://api.zhongchou.cn</online>
<host212>http://*.*.*.*</host212>
<host170>http://app-4-0.api.zhongchou.cn</host170>
</host>
</Setting>
(3)result.txt和token.txt。這兩個文本文件用來存放測試用例執行過程中的一些兒內容。Result.txt文件裏存放的是測試用例執行失敗的信息,token.txt文件中存放的是登錄接口返回的token信息,用於其他的需要登錄的接口,將Token封裝到Http頭中實現登錄狀態下調用接口。
5.4 TestCases文件夾內容
這個文件夾下存放的是具體的測試用例文件,每個測試用例文件是對一個接口的所有測試用例。當然測試用例文件也有一定的命名規則:一般是以接口名來頭,以Test結尾。如:LoginTest.php.
下面我們以一個實例講解一下公用函數的使用:以登錄接口http://api.zhongchou.cn/user/login爲例。
對應的測試用例文件代碼如下:
<?php
/*
*登錄接口http://api.zhongchou.cn/user/login測試用例
*/
require_once 'CommonClass/Require.php';
class login_Test extends PHPUnit_Framework_TestCase{
public function testlogin()
{
/**
* 登錄接口檢測
*/
$rc=new ReadConfig();
$dr=new DataOperations("LoginTest.xml");
$url=$rc->gethost("host170") ."/user/login";//讀取配置文件,選擇運行環境
echo $url;
$get_params=NULL;
$post_params=array('identity'=>$dr->readnodevalue("login","username"),'password'=>$dr->readnodevalue("login","password"));
$content=CurlOperation::Fetch($url, $get_params,$post_params);
$data= json_decode($content,true);
print_r($data);
if($data['errno']==0)
{
$this->assertEquals($dr->readnodevalue("login","serrno"),$data["errno"]);
TextOperation::writetoken($data['data']['token']);
print('接口28:登錄成功------------------OK'.'\n');
}
else
{
print("接口28:登錄失敗---------------Failure!"."\n".$content."\n");
TextOperation::writetxt("接口28:登錄失敗------Failure!"."\n".$content."\n");
$this->assertEquals($dr->readnodevalue("login","serrno"),$data["errno"]);
}
}
代碼講解:
首先引用我們的公用引用文件Require.php文件,將要引用的公用文件全包含進來。然後創建測試用例函數testlogin(),測試用例文件必須以test開頭,否則phpunit不識別。在函數中初始化配置文件類$rc,數據操作類$dr,並且拼接出URL。
由於login接口是Post方式的,從對應的loginTest.xml數據文件中讀取出“identity”和“password”參數對應的值。然後調用公共函數CurlOperation::Fetch()實現調用接口的操作,並將接口返回值json_decode(),然後判斷返回值的errno是否與預期的一樣(從數據文件中讀取的預期值$dr->readnodevalue("login","serrno"))。
5.5 TestData文件夾內容
這個文件夾下存放的是對應測試用例的測試數據文件,一個接口對應一個數據文件,而一個測試用例對應一個數據節點。如login接口的測試用例文件是loginTest.php,對應的測試數據文件是loginTest.xml.其內容如下:
<?xmlversion="1.0" encoding="UTF-8"?>
<!--登錄接口測試數據-->
<TestData>
<login name="登錄接口,正常登錄">
<username>[email protected]</username>
<password>a000000</password>
<serrno>0</serrno>
</login>
<loginuer name="登錄接口,用戶名錯誤">
…….
</loginuer>
</TestData>
這個數據文件中對應兩個測試用例,一個是login正常登錄的測試數據,另一個是loginuer用戶名錯誤的情況,當然這個測試用例數據省略了。以後我們要添加測試用例的時候,只需要在對應的測試數據文件中添加節點和相應的測試數據,在測試用例文件中添加對應的函數即可。
5.6 TestSuites文件夾的內容
TestSuites就是對測試用例的組織,因爲PHPUNIT是利用PHPUnit_Framework_TestSuite來組織測試用例的,我們只需要創建一個類繼承這個類,然後把相應的測試用例文件添加進來即可。
如我們創建OnlineRegression.php文件,內容如下:
<?php
/**
* Created by PhpStorm.
* User: sxf
* Date: 15-4-7
* Time: 下午5:27
*/
require_once'CommonClass/Require.php';
classOnlineResTestSuite extends PHPUnit_Framework_TestSuite {
public function __construct(){
$this->addTestFile('TestCases/LoginTest.php');
/*此處添加所有的測試用例文件
*/
}
public static function suite() {
return new self();
}
}
我們將所有需要執行的測試用例文件,使用$this->addTestFile()函數添加到這個Suite中,然後右擊這個文件,選擇“Run ‘OnlineResTestSuite’”即可執行所有的測試用例。
當然可以根據運行環境啊,測試目的等不同來創建不同的suite文件的。例如,線上迴歸測試環境一般不能產生測試數據,所以有些兒post接口就不能在線上迴歸,此時線上迴歸的suite文件中就不能包含這樣的接口。
5.7 本章小結
本章我們講解了測試用例的架構規劃,測試用例的組織。當然你可以自己規劃一個喜歡的架構,只要方便執行,調試,出錯的時候的定位準確就可以了。我自己喜歡現在的架構,經過我的實際工作中的實踐,還是相當不錯的。通過本章的學習,你可以把自己編寫的單獨的測試用例組織到一起,根據需要設計出合適的suite文件。下章我們將把這些測試用例集成到Jenkins中,實現無人值守的自動化運行。