1. 程式人生 > >LoadRunner指令碼篇

LoadRunner指令碼篇

關  鍵  詞:LoadRunner 效能測試指令碼

摘      要:編寫一個準確無誤的指令碼對效能測試有至關重要的意義。本文介紹LoadRunner的HTTP協議測試指令碼編寫,總結指令碼編寫經驗

1   概述

指令碼錄製編寫是效能測試的一個重要環節。在效能測試過程中,虛擬使用者模擬真實使用者使用被測系統,這個“模擬”的過程正是通過效能測試指令碼來實現的。因此,編寫一個準確無誤的指令碼對效能測試有至關重要的意義。完成效能測試指令碼包括兩個步驟:指令碼錄製和指令碼編寫,本文重點關注指令碼編寫。

2   指令碼錄製

2.1.錄製方式

HTTP協議指令碼錄製可選兩種方式:基於HTML和基於URL。選擇哪種錄製方式的原則如下:基於瀏覽器的HTTP應用系統選擇HTML,基於其他方式的HTTP應用系統選擇URL。

2.2.錄製注意點

取消錄製期間自動關聯功能;

如果部分測試指令碼出現問題,需要重新錄製,可以只錄制存在問題的片斷指令碼,方法是不選中錄製啟動對話方塊中的Record the application startup。

3   指令碼編寫

3.1.常用技術

LoadRunner效能測試指令碼編寫常用的技術包括引數化,關聯,邏輯控制和指令碼模組化。

3.1.1.引數化

引數化就是將指令碼中的常量轉化為變數的過程。通過錄制生成的指令碼所有的資料都是常量,為了達到向伺服器傳送的資料多樣化的目的,需要將一些資料常量轉化為變數。

3.1.2.關聯

關聯就是查詢動態資料,並把查詢到的資料以引數的形式儲存起來。在B/S或者C/S系統中,伺服器返回給客戶端的資料有些是動態改變的,例如客服系統的人工來話流水號和工作流系統的工單流水號。當開啟工單生成頁面後,工單流水號已經從伺服器端獲取到了,而在提交工單步驟,需要將該流水號返回給伺服器。因此,在提交工單之前,在指令碼中必須獲得流水號。獲得流水號的方法就是關聯。

使用關聯功能動態儲存的引數跟直接通過引數化生成的引數是一致的。唯一不同的是,通過引數化生成的引數在指令碼中可以高亮顯示。

3.1.3.邏輯控制

業務系統在實際應用中,業務操作步驟間往往存在邏輯。比如,客服3.0工作流系統,業務代表處理工單,如果待辦區沒有工單等待處理,則先從工單池中提取工單到待辦區,然後進行處理,並且需要優先處理超時或即將超時的工單。在工單處理的效能測試指令碼中,也必須遵從這種業務邏輯。

LoadRunner效能測試指令碼採用C語言,因此指令碼邏輯控制同C語言一致,使用if,switch,while/for/do控制結構。

3.1.4.指令碼模組化

指令碼模組化的目的是:提高指令碼可讀性、可重用性和指令碼生產效率。指令碼模組化的本質是抽取函式,一些很通用的函式甚至可以封裝為DLL。模組化效能測試指令碼的思想跟自動化測試的ActionWord有相似之處。

例如:客服3.0系統的登入功能,無論是工作流、知識庫、公告便籤還是培訓考試,它們都使用相同的登入頁面。我們可以把登入指令碼抽取為一個函式csp_login(char *staffno,char * password),需要登入操作時,不需要錄製和拷貝指令碼,只要呼叫這個函式就可以了。

注意:並不是所有的指令碼程式碼塊都需要做模組化處理,只有那些穩定不變、並且經常用到的程式碼塊才需要做模組化處理。不做得不償失的事。

3.2.典型函式

LoadRunner中,常用的函式有很多,這裡只介紹編寫效能測試指令碼過程中那些必然用到的函式。本文重點關注這些典型函式的應用場合及注意點,至於函式詳細使用說明請參見LoadRunner幫助文件。

3.2.1.事務相關

3.2.1.1. lr_start_transaction/lr_end_transaction

功能:事務開始/結束標記。

應用場合:需要統計某一段程式碼塊執行所需要的時間,這兩個函式需要成對使用。

舉例:工作流系統性能測試中有一個需求,300人線上,提交工單操作平均響應時間在3秒以內,則需要在提交工單請求步驟之前插入lr_start_transaction,提交工單請求步驟之後插入lr_end_transaction。

注意點:這兩個函式只是標記函式,用於標記事務開始/結束,因此可以巢狀使用,即事務中還可以包含子事務。

3.2.1.2. lr_think_time

功能:模擬思考時間,即等待時間。

應用場合:線上使用者測試,為了讓每一個虛擬使用者模擬一個真實使用者的行為,即讓一個虛擬使用者對系統產生的壓力跟真實使用者相當,就必須使用這個函式。這是因為,使用者在使用系統的過程中,從一個操作轉換到另一個操作,是需要時間的,這個時間就是思考時間。

舉例:客服3.0工作流系統線上使用者測試。對於工單查詢操作,輸入查詢條件後提交查詢,從輸入查詢條件至提交查詢的時間間隔就是思考時間。因為LoadRunner無法模擬鍵盤輸入的過程,它只能模擬鍵盤輸入的等待時間,此時需要在提交查詢的那個動作前插入lr_think_time函式。

注意點:在錄製指令碼中,原子事務內不要包含lr_think_time函式,否則該思考時間將被統計到事務響應時間中,造成結果不準確。另外lr_think_time是否啟作用,可通過runtime-seting進行設定。

注:原子事務指那些不能再分割為更小事務的事務,它經常指一個單一的業務操作,通常表現為一個URL請求。

3.2.1.3. lr_rendezvous

功能:在Vuser指令碼中設定集合點。

應用場合:併發測試。

舉例:客服3.0培訓考試系統,100人同時開啟同一份試卷。則需要在開啟試卷的語句前插入lr_rendezvous函式,並在場景中設定集合點策略。

注意點:非併發測試,例如在測試系統的處理能力時,最好不要設定集合點,因為一旦設定了集合點,將導致一些VUser處於等待狀態,在這等待過程中伺服器將是空閒的,這將導致不能準確的測試出伺服器的真實效能水平。集合點更多用於發現系統的併發問題。

3.2.2.引數化/關聯

3.2.2.1.lr_save_string/lr_save_int

功能:將某一字串/整型儲存為引數。

應用場合:有些變數的值通過C語言生成,之後在測試指令碼中要使用這些變數。

舉例:客服3.0業務配置臺系統增加業務代表操作,業務代表的工號和姓名使用C語言函式生成。工號和姓名分別儲存在staff_no和staff_name變數中,則在指令碼中可以使用lr_save_int(staff_no,"staffno"),lr_save_string(staff_name, "stafffname")將工號和姓名引數化。

注意點:無。

3.2.2.2.web_reg_save_param

功能:在伺服器返回的文字中查詢一個或者多個字串,並將搜尋到的字串值儲存在引數中。

應用場合:在B/S或者C/S系統中,伺服器返回給客戶端的資料有些是動態改變的,在指令碼的下一個步驟中,需要使用該動態資料。這時,就需要使用關聯獲得該動態資料。

舉例:客服3.0工作流系統,工單辦理每次都從待辦區中開啟第一條工單,為開啟第一條工單,需要獲取第一條工單的完整URL(包括URL中的parameter及其值),而每一次進入待辦區,第一條工單有可能是不一樣的。為獲取第一條工單的URL,將開啟工單的URL做關聯。已知開啟待辦區操作獲得的HTML有如下片斷:

<a href="#"onclick="javascript:openseviceforprocess('/iwflow/FindJspID.jsp?serialNo=2008092200000033&serviceID=0099&nodeID=140004&dealID=2008092200000056&hisFlag=0&skillID=020401&dealSkillID=020101&dealStaff=1200','false');">

可在開啟待辦區的操作前插入如下語句:

web_reg_save_param("tt_url", "LB=javascript:openseviceforprocess('","RB=','false')", "Ord=1","IgnoreRedirections=Yes", "Search=Body","RelFrameId=1", LAST );

執行指令碼後,tt_url的值為:

/iwflow/FindJspID.jsp?serialNo=2008092200000033&serviceID=0099&nodeID=140004&dealID=2008092200000056&hisFlag=0&skillID=020401&dealSkillID=020101&dealStaff=1200

這個URL就是開啟第一條工單的URL,有了URL,便可開啟工單。

注意點:

(1)LoadRunner工具只能識別文字,在HTTP協議中只能識別HTML文件,因此關聯的依據是HTML原始碼,而不是經過瀏覽器解析後的視覺化文字。這一點很重要。

(2)關聯還能將多個匹配的引數儲存在陣列中,方法是指定ORD的屬性值為ALL,之後通過“{引數名_1}”, “{引數名_2}”, “{引數名_3}”格式可獲得陣列元素的值。

(3)該函式有一個屬性NOTFOUND,預設值為ERROR,也就是說,如果找不到要查詢的資料,將報出錯誤,在必要的時候,例如指令碼邏輯控制需要,可以將NOTFOUND的屬性值設為WARNING,這樣LoadRunner將不產生錯誤。

3.2.2.3.lr_save_searched_string

功能:在某一個字元緩衝區中搜索指定的字串,並將搜到的字串儲存在引數中。

應用場合:可配合LoadRunner的關聯功能,靈活獲取伺服器端返回的資料。

舉例:客服3.0工作流系統,工單處理每次都從待辦區中開啟第一條工單,開啟工單的URL已經通過關聯儲存在tt_url引數中,在工單處理提交時,需要使用serviceNo,serviceID,nodeID,dealID,tt_url的值如下:

/iwflow/FindJspID.jsp?serialNo=2008092200000033&serviceID=0099&nodeID=140004&dealID=2008092200000056&hisFlag=0&skillID=020401&dealSkillID=020101&dealStaff=1200

可使用以下函式儲存serviceNo,serviceID,nodeID,dealID的值。

//儲存serialNo,serviceID,nodeID,dealID引數

int getTTData(){

 int i = 0;int j=0;

char *tt_url = lr_eval_string("{tt_url}");

 int len= strlen(tt_url);

 while(tt_url[i]!='='){i++;} while(tt_url[j]!='&'){j++;}

 lr_save_searched_string(tt_url,len,0,"serialNo",1,j-i-1,"serialNo");

 i++;j++;while(tt_url[i]!='='){i++;} while(tt_url[j]!='&'){j++;}

lr_save_searched_string(tt_url,len,0,"serviceID",1,j-i-1,"serviceID");

  i++;j++;while(tt_url[i]!='='){i++;}  while(tt_url[j]!='&'){j++;}

 lr_save_searched_string(tt_url,len,0,"nodeID",1,j-i-1,"nodeID");

  i++;j++; while(tt_url[i]!='='){i++;}  while(tt_url[j]!='&'){j++;}

lr_save_searched_string(tt_url,len,0,"dealID",1,j-i-1,"dealID");

return 0;

}

注意點:

3.2.2.4.lr_save_datetime

功能:將時間儲存為引數。

應用場合:應用系統需要把時間資料提交給伺服器端。

舉例:客服3.0工作流系統活動工單查詢,預設查詢從當天開始的最近三天工單。 則查詢的開始時間和結束時間可用lr_save_datetime獲取。

lr_save_datetime("%y-%m-%d00:00", DATE_NOW-2*ONE_DAY, "queryBeginTime");    

lr_save_datetime("%y-%m-%d23:59", DATE_NOW, "queryEndTime");

注意點:

3.2.2.5. web_save_timestamp_param

功能:將當前時間戳儲存為引數。

應用場合:應用系統需要把時間戳提交給伺服器端。

舉例:多媒體坐席客戶端,在向MClient提交資訊時,需要附帶客戶端的時間戳,則可以使用該函式獲取當前時間戳。

注意點:與lr_save_datetime不同的是,本函式儲存的是時間戳,而lr_save_datetime儲存的是日期和時間。

3.2.2.6.lr_eval_string

功能:將某一字串中包含的所有引數替換為真實值,並返回替換後的字串。

應用場合:欲檢視某一引數的值,可使用該函式。

舉例:客服3.0工作流系統,生成工單時開啟工單頁面準備工單提交,提交之前想檢視已通過關聯儲存的serialNo引數的值。方法如下:

lr_output_message(lr_eval_string("TheserialNo is {serialNo}"));

注意點:如果不存在該引數,將把“{引數名}”當作普通字串輸出。如本例,如果不存在serialNo引數,則輸出:The serialNo is {serialNo}。

3.2.3.驗證點

3.2.3.1.web_reg_find

功能:在HTML文件中查詢指定的字串。

應用場合:該函式是檢查點函式,在指令碼中需要插入檢查點的地方使用。

舉例:客服3.0工作流系統,提交工單生成後,需要驗證工單是否提交成功。則可根據頁面提示“工單生成成功”進行驗證。在提交生成工單步驟前插入:

web_reg_find("Text=工單生成成功",LAST);

注意點:該函式是註冊型引數,需要在請求伺服器資料步驟之前插入該函式。與該函式功能類似的函式是web_find,但是web_find只對HTML方式的指令碼起作用,對URL方式指令碼則不起作用,而且web_find函式效率低下,已被廢棄。

3.2.3.2. web_image_check

功能:判斷某一個圖片是否存在HTML頁面中。

應用場合:同web_reg_find函式一樣,該函式也是檢查點函式,在指令碼中需要插入檢查點的地方使用。

舉例:客服3.0培訓考試系統併發測試,50個人同時開啟試卷,為了驗證開啟試卷成功,根據試卷中的圖片public/images/onexam.gif進行驗證:

web_image_check("web_image_check","Src=public/images/onexam.gif",LAST);

注意點:要使該函式生效,需要在runtime-seting中將開啟。與web_reg_find不一樣的是,該函式不是註冊型函式,因此需要在請求返回步驟之後插入該函式。上文提過,LoadRunner只能識別文字,因此web_image_check函式其本質仍然是文字驗證,完全可以用web_reg_find替代,而且強烈推薦使用web_reg_find作為檢查點函式。

3.2.4.日誌輸出

3.2.4.1. lr_output_message

功能:將VUser的訊息列印到日誌檔案和輸出視窗中,列印的訊息帶有指令碼行資訊。

應用場合:方便檢視執行資訊,輔助問題定位。

舉例:客服3.0系統,登入工號已經引數化,除錯指令碼時將當前的登入工號輸出到Replay Log視窗中。程式碼如下:

lr_output_message("Thestaffno is %s",lr_eval_string("{staffno}"));

注意點:與該函式具有類似功能的還有:lr_debug_message,lr_log_message lr_message,lr_error_message它們之間的不同之處這裡不作詳細介紹,請參見LoadRunner幫助文件。

3.2.4.2. lr_vuser_status_message

功能:將VUser的訊息輸出到場景執行的VUser狀態視窗。

應用場合:將一些關鍵資訊輸出到VUser執行狀態視窗,方便場景執行時檢視。

舉例:在場景執行過程中,出現了錯誤。根據錯誤視窗提示,該錯誤屬於VUser ID為2的虛擬使用者,為了方便將系統登入使用者名稱與VUser ID對應起來,以方便問題定位。可以使用以下程式碼:

lr_vuser_status_message("Thelogin username is %s", lr_eval_string("{username}"));

場景執行時,可方便檢視到VUserID與登入使用者名稱的對應關係,如下圖:

注意點:

3.2.5.其它實用函式

3.2.5.1. lr_get_vuser_ip

功能:獲得VUser的IP地址。

應用場合:在使用IP欺騙時,為了驗證IP欺騙是否成功,可以使用該函式。

舉例:在場景執行過程中,將每一個VUser的IP在VUser執行狀態視窗中顯示出來。

char *ip;

ip = lr_get_vuser_ip();

if (ip)

{

  lr_vuser_status_message("The IP addressis %s", ip);

}

注意點:為了使IP欺騙成功,使用IP欺騙嚮導設定好IP後,還要將開啟才可。

3.2.5.2. lr_load_dll

功能:載入外部DLL。

應用場合:指令碼需要使用外部DLL時,使用該函式載入DLL。

舉例:函式getDateTime(char * time,int seconds,char * resultTime)已封裝在timeutil.dll中,getDateTime的功能是根據傳入的日期字串time(如2008-09-24 16:56:24),秒偏移量seconds,計算返回結果日期字串resultTime,程式碼如下:

int hours =atoi(lr_eval_string("hours"));

char acceptEndTime[20];

lr_load_dll("../timeutil.dll");

getDateTime(lr_eval_string("{acceptBeginTime}"),3600*hours,acceptEndTime);

注意點:該函式為LoadRunner提供了呼叫外部介面的能力。

3.3.封裝,構建可重用指令碼

3.3.1.簡單函式封裝

LoadRunner使用C語言作為指令碼,因此只要是合法的C程式碼都可以在LoadRunner中執行。為了提高指令碼可讀性和指令碼生產效率,有必要將效能測試指令碼模組化。

客服3.0工作流系統,查詢工單池是一個很常見的操作。我們可以把查詢工單池操作封裝為一個queryTTPool函式,函式體如下,在指令碼中,將所有的查詢工單池操作替換為queryTTPool函式呼叫,提高了指令碼的可讀性:

//查詢工單池

int queryTTPool(char* nodeType){

   lr_save_string(nodeType,"nodeType");

   lr_save_datetime("%Y-%m-%d 00:00",DATE_NOW-2*ONE_DAY, "acceptBeginTime");

   lr_save_datetime("%Y-%m-%d 23:59", DATE_NOW,"acceptEndTime");

   lr_start_transaction("WF_查詢工單池工單");

   web_submit_data("IWFController",

        "Action=http://{wf_sysurl}/IWFController",

        "Method=POST",

        "RecContentType=text/html",

         "Referer=http://{wf_sysurl}/iwflow/common/UnitQueryDealForm.jsp?pageNo=1",            "Snapshot=t32.inf",

        "Mode=HTML",

        ITEMDATA,

        "Name=ACTIONID","Value=UniteQueryDealAction", ENDITEM,

        "Name=pageNo", "Value=1", ENDITEM,

        "Name=sortType", "Value=", ENDITEM,

        "Name=sortField", "Value=", ENDITEM,

        "Name=reSortFlag", "Value=", ENDITEM,

        "Name=acceptPhone", "Value=", ENDITEM,

        "Name=serialNo", "Value=", ENDITEM,

        "Name=serialFlag", "Value=0", ENDITEM,

        "Name=serviceName", "Value=", ENDITEM,

        "Name=serviceID", "Value=", ENDITEM,

        "Name=acceptBeginTime","Value={acceptBeginTime}", ENDITEM,

        "Name= acceptEndTime ", "Value={acceptEndTime}",ENDITEM,

        "Name=urgentID", "Value=", ENDITEM,

        EXTRARES,

        "Url=/iwflow/image/kms-1_23.gif", ENDITEM,

        "Url=/iwflow/buttons/obtainProcess-2.gif", ENDITEM,

        LAST);

   lr_end_transaction("WF_查詢工單池工單",LR_AUTO);

}

3.3.2.DLL封裝

使用DLL有很多好處。高度重用的函式製作成DLL,方便指令碼呼叫。將與IVR互動的訊息函式封裝成DLL後,便可利用LoadRunner測試IVR效能。製作DLL可以選擇VC或者MinGW Developer Studio等工具,至於DLL的製作細節,本文不作介紹,請參見相關指導書。

4   指令碼除錯

LoadRunner的VUser Generator本身的除錯功能比較弱,只能設定斷點,無法單步跟蹤。當指令碼出現問題時,可以使用lr_debug_message,lr_output_message,lr_eval_string等函式協助定位。

指令碼錯誤大部分原因都是向伺服器傳送的資料不對,因此還可以利用HttpAnalyzer工具進行HTTP協議跟蹤,通過比較LoadRunner傳送的資料和瀏覽器傳送的資料,便能很快定位出問題根源。