Vuser 發生器(Visual User Generator,VuGen),主要通過捕獲客戶端向服務器發送的HTTP請求,將這些請求錄制成腳本,在回放時將捕獲的HTTP請求再次發送,以達到模擬客戶行為的目的。
下面具體介紹一下如何使用,篇幅比較長,請耐心看下去:
腳本錄制
這里以HP自帶的在線訂票網站進行錄制,依次打開:開始|所有程序|HP Loadrunner|Samples|web|HP Tours Web Application(注:打開網站前,要先啟動同目錄下的Web Server 服務,否則進不了)。
首先得注冊一個賬號,才能進行后續的操作。
注冊完后,打開VuGen應用,依次打開:開始|所有程序|HP Loadrunner|Applications|Visual User Generator(如圖1所示)。
圖1(注:在打開應用過程中如果遇到問題,可以嘗試右鍵‘以管理員身份運行’)
點擊界面中的New Script圖標按鈕,即創建新腳本,會彈出一個對話框(如圖2所示),可以看到loadrunner提供3種選擇協議的方式:單協議腳本、多協議腳本和最近使用過的協議。
圖2
在錄制腳本過程中,涉及的關鍵點是如何選擇錄制協議,選擇的協議將直接影響錄制后的腳本是否理想。那到底怎么知道被測對象用的是什么通信協議呢?常用方法有幾種:
1)直接跟開發確認數據通信所采用的協議;
2)通過概要設計或詳細設計手冊獲知所使用的協議;
3)使用協議分析工具捕獲通信時的數據包並進行分析(注:一定要摒除底層協議,不要被底層協議所迷惑);
4)根據以往測試經驗來判斷被測對象所采用的協議,這種方法有時不一定准確。
那這里順便貼上常用的協議分類表,僅供參考(如圖3所示)。
圖3
這里呢就以比較常用的Web(HTTP/HTML)協議為例進行錄制。
VuGen 錄制瀏覽器主要是通過代理的方式來實現的,即以VuGen 作為代理來訪問目標服務器,這樣就可以捕獲客戶端與服務器之間通過的數據包(默認是使用自帶的IE瀏覽器,使用其他瀏覽器錄制容易出現HTTP請求被丟失的現象,所以盡量使用IE瀏覽器進行錄制)。
錄制時系統彈出一個錄制窗口(如圖4所示),具體說明如下:
圖4
1)application type:應用類型(比如BS或CS);
2)program to record:錄制的程序(即用什么瀏覽器來錄制,前面也提到過,最好默認使用自帶的IE);
3)URL:被錄制應用的地址;
4)working directory:存放路徑;
5)record into action:表示將錄制的代碼放到哪個部分;(注:loadrunner生成的代碼由三部分組成,vuser_int,aciton和vuser_end。一般情況下都是將生成的代碼放在action部分,因為vuser_int和 vuser_end兩部分的代碼只會執行一次,這樣會出現這種問題,客戶的並發虛擬用戶只執行一次,執行完成一次后再也不執行,這樣就沒有HTTP請求給服務器,也即服務器沒有壓力)
6)Record the application startup:表示應用程序一啟動,就立即開始錄制;如果不選,則應用程序啟動后,VuGen會彈出一個對話框,並且暫時不會進行錄制,當用戶操作應用程序到需要錄制的地方時,單 擊record按鈕,才開始錄制。
開始錄制后,會出現一個工具欄(如圖5所示),從左到右依次代表開始錄制、停止錄制、暫停錄制、新建action、在腳本與錄制界面之間切換、添加開始事務標識、添加結束事務標識、設置集合點和添加注釋。
圖5
錄制完后,單擊'停止錄制'按鈕結束錄制,這時VuGen會自動生成一個腳本。可以看出LR生成的腳本都是由函數組成的(如圖6所示)。
圖6
recording options 設置
在進行錄制時,首先要對錄制的一些一些參數進行設置,只有將這些參數設置好,才能錄制並生成需要的腳本。
首先是recording options設置(如圖7所示),需要注意的設置項有recording選項卡,advanced選項卡和correlation選項卡。
圖7
1、recording選項卡
recording level包含兩種錄制模式:HTML-based script和URL-based script。(如圖8所示)
圖8
關於HTML-based script腳本方式又有兩種:
1)模擬用戶錄制行為,即GUI錄制,把用戶每一步的操作顯示出來,最后生成的腳本非常直觀,並且會將上下文的一些敏感信息記錄下來。它創建URL(web_url)、link(web_link)、image(web_image)和提交表單(web_submit_form)。代碼如下:
在錄制時只做了兩個操作,生成的代碼也只有兩個函數,也即這種錄制模式只錄制用戶的操作,其他的內容不會被錄制,使用的提交信息函數為web_submit_form(),該函數運行時,首先在頁面上去查找表單,再提交數據
友情吐槽一下:用這個模式錄制,本來使用的函數應該是web_submit_form()的,但不知道為什么,我的機子錄制了很多遍,最后生成的都是使用的web_submit_data()函數,所以下面的這段代碼是我錄制后參照書本所描述的修改了一下得來的。

web_url("WebTours", "URL=http://127.0.0.1:1080/WebTours/", "Resource=0", "RecContentType=text/html", "Referer=", "Snapshot=t1.inf", "Mode=HTML", LAST); web_submit_form("login.pl", "Snapshot=t5.inf", ITEMDATA, "Name=username", "Value=test1", ENDITEM, "Name=password", "Value=test1", ENDITEM, "Name=login.x", "Value=0", ENDITEM, "Name=login.y", "Value=0", ENDITEM, LAST); return 0;
2)錄制所有links、images和URL,但不創建web_link、web_image和提交表單(web_submit_form)。這種錄制方式生成的腳本不直觀。如下代碼:
同樣的提交登錄的信息,但使用的是web_submit_data(),該函數是直接向服務器發送要提交的數據的。

web_url("WebTours", "URL=http://127.0.0.1:1080/WebTours/", "TargetFrame=", "Resource=0", "RecContentType=text/html", "Referer=", "Snapshot=t1.inf", "Mode=HTML", LAST); lr_think_time(10); web_submit_data("login.pl", "Action=http://127.0.0.1:1080/WebTours/login.pl", "Method=POST", "TargetFrame=", "RecContentType=text/html", "Referer=http://127.0.0.1:1080/WebTours/nav.pl?in=home", "Snapshot=t5.inf", "Mode=HTML", ITEMDATA, "Name=userSession", "Value=120572.007128392zcAVQccpzVzzzzzHDHcVVpfDfH", ENDITEM, "Name=username", "Value=test1", ENDITEM, "Name=password", "Value=test1", ENDITEM, "Name=JSFormSubmit", "Value=off", ENDITEM, "Name=login.x", "Value=0", ENDITEM, "Name=login.y", "Value=0", ENDITEM, LAST); return 0;
在錄制過程中可能會錄制到一些非HTML的元素(如XML、JS),主要用於包含去獲取自己的一些資源,如JS文件用於調用加載多個圖片。對於這類非HTML的元素,錄制時有3種方式:
1)錄制時對於非HTML資源並不會生成一個新的功能。它列出所有相關資源的參數,如web_url、web_link和web_submit_data。這些功能的參數使用EXTRARES表示,如下代碼:

web_url("WebTours", "URL=http://127.0.0.1:1080/WebTours/", "Resource=0", "RecContentType=text/html", "Referer=", "Snapshot=t1.inf", "Mode=HTML", EXTRARES, "Url=http://act.cmcmcdn.com/upload/201507/8afc2fe48db9060fe1bdda2089e1d950.png", ENDITEM, "Url=http://act.cmcmcdn.com/upload/201507/3b491068507d8f85ea7b35d756da7215.png", ENDITEM, LAST);
2)在一個組中記錄這些單獨的步驟,為每個非HTML資源創建一個新的功能,如下代碼:

web_url("WebTours", "URL=http://127.0.0.1:1080/WebTours/", "TargetFrame=", "Resource=0", "RecContentType=text/html", "Referer=", "Snapshot=t1.inf", "Mode=HTML", LAST); web_concurrent_start(NULL); web_url("8afc2fe48db9060fe1bdda2089e1d950.png", "URL=http://act.cmcmcdn.com/upload/201507/8afc2fe48db9060fe1bdda2089e1d950.png", "TargetFrame=", "Resource=1", "RecContentType=image/png", "Referer=http://127.0.0.1:1080/WebTours/", "Snapshot=t5.inf", LAST); web_url("3b491068507d8f85ea7b35d756da7215.png", "URL=http://act.cmcmcdn.com/upload/201507/3b491068507d8f85ea7b35d756da7215.png", "TargetFrame=", "Resource=1", "RecContentType=image/png", "Referer=http://127.0.0.1:1080/WebTours/", "Snapshot=t6.inf", LAST); web_concurrent_end(NULL);
3)不記錄,對於非HTML元素不記錄,如下代碼:

web_url("WebTours", "URL=http://127.0.0.1:1080/WebTours/", "Resource=0", "RecContentType=text/html", "Referer=", "Snapshot=t1.inf", "Mode=HTML", LAST);
URL-based script方式
將每條客戶端發出的請求錄制成一條語句,對LR來說,在該模式下,一條語句只能建立一個到服務器的連接,並將通信過程中的很多隱藏信息都錄制出來(如session、cookie)。LR提供了web_concurrent_start()和web_concurrent_end()函數模擬URL-based scriptr的工作方式。如下代碼:

web_url("WebTours", "URL=http://127.0.0.1:1080/WebTours/", "Resource=0", "RecContentType=text/html", "Referer=", "Snapshot=t1.inf", "Mode=HTTP", LAST); web_url("header.html", "URL=http://127.0.0.1:1080/WebTours/header.html", "Resource=0", "RecContentType=text/html", "Referer=http://127.0.0.1:1080/WebTours/", "Snapshot=t2.inf", "Mode=HTTP", LAST); web_url("www.baidu.com", "URL=http://www.baidu.com/", "Resource=0", "RecContentType=text/html", "Referer=", "Snapshot=t3.inf", "Mode=HTTP", LAST); web_url("webtours.png", "URL=http://127.0.0.1:1080/WebTours/images/webtours.png", "Resource=1", "RecContentType=image/png", "Referer=http://127.0.0.1:1080/WebTours/header.html", "Snapshot=t4.inf", LAST); web_url("hp_logo.png", "URL=http://127.0.0.1:1080/WebTours/images/hp_logo.png", "Resource=1", "RecContentType=image/png", "Referer=http://127.0.0.1:1080/WebTours/header.html", "Snapshot=t5.inf", LAST); web_url("welcome.pl", "URL=http://127.0.0.1:1080/WebTours/welcome.pl?signOff=true", "Resource=0", "RecContentType=text/html", "Referer=http://127.0.0.1:1080/WebTours/", "Snapshot=t6.inf", "Mode=HTTP", LAST); web_concurrent_start(NULL); web_url("home.html", "URL=http://127.0.0.1:1080/WebTours/home.html", "Resource=0", "RecContentType=text/html", "Referer=http://127.0.0.1:1080/WebTours/welcome.pl?signOff=true", "Snapshot=t7.inf", "Mode=HTTP", LAST); web_url("nav.pl", "URL=http://127.0.0.1:1080/WebTours/nav.pl?in=home", "Resource=0", "RecContentType=text/html", "Referer=http://127.0.0.1:1080/WebTours/welcome.pl?signOff=true", "Snapshot=t8.inf", "Mode=HTTP", LAST); web_concurrent_end(NULL); web_url("mer_login.gif", "URL=http://127.0.0.1:1080/WebTours/images/mer_login.gif", "Resource=1", "RecContentType=image/gif", "Referer=http://127.0.0.1:1080/WebTours/nav.pl?in=home", "Snapshot=t9.inf", LAST); web_concurrent_start(NULL); web_url("8afc2fe48db9060fe1bdda2089e1d950.png", "URL=http://act.cmcmcdn.com/upload/201507/8afc2fe48db9060fe1bdda2089e1d950.png", "Resource=1", "RecContentType=image/png", "Referer=http://127.0.0.1:1080/WebTours/", "Snapshot=t10.inf", LAST); web_url("3b491068507d8f85ea7b35d756da7215.png", "URL=http://act.cmcmcdn.com/upload/201507/3b491068507d8f85ea7b35d756da7215.png", "Resource=1", "RecContentType=image/png", "Referer=http://127.0.0.1:1080/WebTours/", "Snapshot=t11.inf", LAST); web_concurrent_end(NULL); lr_think_time(12); web_submit_data("login.pl", "Action=http://127.0.0.1:1080/WebTours/login.pl", "Method=POST", "RecContentType=text/html", "Referer=http://127.0.0.1:1080/WebTours/nav.pl?in=home", "Snapshot=t12.inf", "Mode=HTTP", ITEMDATA, "Name=userSession", "Value=120572.172620494zcAVQDVpVcQVzzzHDHcVVpfizHHf", ENDITEM, "Name=username", "Value=test1", ENDITEM, "Name=password", "Value=test1", ENDITEM, "Name=JSFormSubmit", "Value=off", ENDITEM, "Name=login.x", "Value=0", ENDITEM, "Name=login.y", "Value=0", ENDITEM, LAST); web_concurrent_start(NULL); web_url("nav.pl_2", "URL=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home", "Resource=0", "RecContentType=text/html", "Referer=http://127.0.0.1:1080/WebTours/login.pl", "Snapshot=t13.inf", "Mode=HTTP", LAST); web_url("login.pl_2", "URL=http://127.0.0.1:1080/WebTours/login.pl?intro=true", "Resource=0", "RecContentType=text/html", "Referer=http://127.0.0.1:1080/WebTours/login.pl", "Snapshot=t14.inf", "Mode=HTTP", LAST); web_concurrent_end(NULL); web_concurrent_start(NULL); web_url("flights.gif", "URL=http://127.0.0.1:1080/WebTours/images/flights.gif", "Resource=1", "RecContentType=image/gif", "Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home", "Snapshot=t15.inf", LAST); web_url("signoff.gif", "URL=http://127.0.0.1:1080/WebTours/images/signoff.gif", "Resource=1", "RecContentType=image/gif", "Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home", "Snapshot=t16.inf", LAST); web_url("itinerary.gif", "URL=http://127.0.0.1:1080/WebTours/images/itinerary.gif", "Resource=1", "RecContentType=image/gif", "Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home", "Snapshot=t17.inf", LAST); web_url("in_home.gif", "URL=http://127.0.0.1:1080/WebTours/images/in_home.gif", "Resource=1", "RecContentType=image/gif", "Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home", "Snapshot=t18.inf", LAST); web_concurrent_end(NULL); return 0;
關於URL高級設置有兩種方式:
1)create concurrent groups for resources their source HTML page。將捕獲所有HTML頁面的資源並保存在並發組中(並發組使用web_concurrent_start和web_concurrent_end satements 兩個函數表示,如上面的代碼,就是使用了這種方式,故不再重復貼代碼了),如果不選中該選項,HTML頁面資源將會分成獨立的、單獨的web_url步驟,但不放入並行組中。
2)use web_custom_request only。如果錄制的是非瀏覽器的應用程序,可以設置VuGen自定義HTTP請求。在LR中使用web_custom_request函數來實現,代碼如下:

web_custom_request("WebTours", "URL=http://127.0.0.1:1080/WebTours/", "Method=GET", "Resource=0", "RecContentType=text/html", "Referer=", "Snapshot=t1.inf", "Mode=HTTP", LAST);
相對比兩種錄制模式,可參考下面的原則:
a.基於瀏覽器的應用程序推薦用HTML;反之,則用URL
b.如果基於瀏覽器的應用程序包含了JS,並且該腳本向服務器發送了請求,比如DataGrid的分布按鈕,推薦用URL
c.基於瀏覽器的應用程序中使用了HTTPS安全協議,建議使用URL。
如果使用HTML模式錄制后不能成功回話,可以考慮改用URL模式來錄制,因為這種情況多是同上面所列舉的原因所引起的。
2、advanced選項卡
是設置腳本回話的高級選項。(如圖9所示)
圖9
1)save snapshot resources locally:表示運行結果中保存一個快照
2)add comments to script for HTTP errors while recording:表示出現錯誤時會自動添加注釋。在Heades可以選擇需要錄制的heades,以便服務器能夠正確處理編輯信息。需要注意的是accept-language選項,像websphere這類服務器會根據HTTP請求中的header來確定編碼。
3、correlation選項卡
用來對腳本中的關聯屬性進行設置(如圖10所示)。LR包括兩種規則:內建規則和自定義規則;LR會默認自帶一些內建規則。在錄制時選中需要的關聯規則,錄制腳本過程中LR會自動匹配需要關聯的規則,並生成關聯函數。如果當前這些關聯規則無法滿足錄制的需求,那么可以單擊new application按鈕來新建一個關聯,再單擊new rule按鈕為該關聯新建一個規則。
圖10
run-time settings 設置
主要用於設置在腳本運行過程中腳本運行的策略,需要注意的設置項有run logic選項卡、pacing選項卡、think time選項卡和miscellaneous選項卡,具體解釋可查看我的另一篇博客,都有詳細的描述。在這里就不再贅述了。(附上博客鏈接:http://www.cnblogs.com/Chilam007/p/6114626.html)
腳本完善
腳本錄制完成並不代表這些腳本很接近用戶的真實使用情況,為了使這些腳本更接近用戶的真實使用情況,需要對腳本進行完善。
首先,為了衡量服務器對某個動作處理的響應時間,需要定義事務(transaction)。例如一個登錄動作,為了衡量多用戶同時執行登錄服務器的響應時間,可以將此操作定義為一個事務。其次,為了衡量加重負載的情況下服務器的性能情況,需要插入集合點。例如為了模擬50個用戶同時登錄的情況,要做到真正的並發,就必須添加一個集合點,用來衡量服務器的性能。
最后,在錄制腳本的過程中添加注釋,以增強腳本的可讀性 。
1、插入事務
事務即客戶要實現的業務,事務響應時間的原理是將業務操作結束時的時間點與業務開始時的時間點的差值,反應了業務的響應時間,也即是事務由兩部分組成:開始事務函數和結束事務函數。
插入事務有兩種方法:一種是在錄制過程中插入開始和結束事務點;另一種是在編輯腳本時插入。一般情況下是在錄制過程中插入。
錄制過程中需要插入事務點時,單擊錄制工具欄中的‘插入開始事務點’按鈕,輸入開始事務名稱(要遵守腳本編輯命名規則)。事務的開始點與事務的結束點具有一一對應的特點,即在腳本中插入了一個事務開始點后一定要在接下來的過程中插入結束事務點,插入結束事務點的操作與插入開始事務點的操作一致,當該業務流程執行完畢后需要添加結束事務點,在工具欄中點擊‘插入結束事務點’即可。
如完成后才插入的話,在生成的腳本中找到要插入開始事務點的地方,選擇insert->start transaction,在彈出的對話框中輸入開始事務的名稱;找到要插入結束事務點的地方。(注:同樣的操作,但在‘結束事務’對話框中,比在腳本錄制過程中插入結束事務點時的對話框中多出了一項transaction status,如圖11所示)。
1)事務的狀態被自動設置,如果事務執行成功,狀態設置為PASS;如果事務執行失敗,狀態設置為FAIL;如果事務異常中斷,狀態設置為STOP。
2)事務執行成功,代碼返回的狀態是PASS。
3)事務執行失敗,代碼返回的是FAIL。
4)事務異常中斷,代碼返回的狀態是STOP。
為什么需要事務狀態?因為在測試過程中不單需要獲取事務的響應時間,還需要確定事務是否成功,如果響應時間很短,但事務都失敗了,那也是無法滿足客戶需求的。
圖11
回放腳本后,事務結束狀態為PASS並不一定代表業務一定做成功了(比如將登錄腳本的賬戶密碼改為錯誤的,可看下面貼的代碼),因為LR在自動判斷事務結束狀態時是以結束函數是否運行為標准,即只要結束事務函數運行,那么就將事務的結束狀態置為PASS,反之將事務的結束狀態置為FAIL。正常情況應該是登錄后,先檢查登錄賬戶名是否正確,如果檢查到正確后才將事務的結束狀態置為PASS,這樣才能保證業務成功了,否則做性能測試根本毫無意義。所以一般情況下都需要先設置檢查點后,再根據檢查點來判斷事務是否成功,這樣才比較合理。

web_url("WebTours", "URL=http://127.0.0.1:1080/WebTours/", "Resource=0", "RecContentType=text/html", "Referer=", "Snapshot=t1.inf", "Mode=HTML", EXTRARES, "Url=http://act.cmcmcdn.com/upload/201507/8afc2fe48db9060fe1bdda2089e1d950.png", ENDITEM, "Url=http://act.cmcmcdn.com/upload/201507/3b491068507d8f85ea7b35d756da7215.png", ENDITEM, LAST); lr_start_transaction("登錄"); lr_think_time(16); web_submit_data("login.pl", "Action=http://127.0.0.1:1080/WebTours/login.pl", "Method=POST", "RecContentType=text/html", "Referer=http://127.0.0.1:1080/WebTours/nav.pl?in=home", "Snapshot=t3.inf", "Mode=HTML", ITEMDATA, "Name=userSession", "Value=120579.391332523zcAcDAcpttVzzzzHDHcVDpiVtzf", ENDITEM, "Name=username", "Value=test1", ENDITEM, "Name=password", "Value=test1", ENDITEM, "Name=JSFormSubmit", "Value=off", ENDITEM, "Name=login.x", "Value=45", ENDITEM, "Name=login.y", "Value=12", ENDITEM, LAST); lr_end_transaction("登錄",LR_AUTO); return 0; 注:回放這個腳本時,事務的結束狀態為PASS,這是正確的,因為這個腳本是可以正確的登錄的。

web_submit_data("login.pl", "Action=http://127.0.0.1:1080/WebTours/login.pl", "Method=POST", "RecContentType=text/html", "Referer=http://127.0.0.1:1080/WebTours/nav.pl?in=home", "Snapshot=t3.inf", "Mode=HTML", ITEMDATA, "Name=userSession", "Value=120579.391332523zcAcDAcpttVzzzzHDHcVDpiVtzf", ENDITEM, "Name=username", "Value=error1", ENDITEM, "Name=password", "Value=test1", ENDITEM, "Name=JSFormSubmit", "Value=off", ENDITEM, "Name=login.x", "Value=45", ENDITEM, "Name=login.y", "Value=12", ENDITEM, LAST); 注:下面是回放日志,可以看到雖然是錯誤的賬號,但仍被置為PASS狀態,這是不正確的。 Action.c(4): web_url("WebTours") was successful, 9467 body bytes, 2222 header bytes [MsgId: MMSG-26386] Action.c(16): Notify: Transaction "登錄" started. Action.c(20): web_submit_data("login.pl") was successful, 795 body bytes, 225 header bytes [MsgId: MMSG-26386] Action.c(36): Notify: Transaction "登錄" ended with "Pass" status (Duration: 0.3466 Wasted Time: 0.0031). Ending action Action.
2、插入集合點
集合點是指在腳本中插入集合點函數(lr_rendezvous),當腳本運行到集合點函數時,將停止運行並等待其允許運行的條件(其允許運行條件即為集合點策略)達到后才釋放集合點開始運行。也即在性能測試過程中如果需要保證虛擬用戶真正並發,即必須添加集合點。
集合點可以在錄制過程中插入,也可以在錄制完成后插入,但只能插入action部分的腳本中,不能插入vuser_int和vuser_end兩部分腳本中。
錄制過程中需要插入集合點的位置時,單擊錄制工具欄中的‘插入集合點’按鈕即可插入集合點;如完成后才插入的話,可選擇insert->rendezvous,在彈出的‘插入集合點’對話框中輸入要集合點名稱。(注意命名時要遵守腳本編輯命名規則,使其具有代表性)。
3、插入注釋
注釋可以在錄制腳本過程中插入,也可以在腳本錄制完成后插入。
錄制過程中需要對錄制的腳本進行注釋時,單擊錄制工具欄中的‘插入注釋’按鈕即可插入注釋;如完成后才插入的話,可選擇insert->comment,在彈出的‘插入注釋’對話框中輸入要注釋的內容。
備注:文字講解來自《深入性能測試--LoadRunner性能測試、流程、監控、調優全程實戰剖析》(黃文高、何月順編著)一書,我是新手,參照此教程做了下實踐,順便將學到的東西寫下來。