加油!!

发布新日志

  • qtp学习笔记

    2011-03-29 10:10:54


       文章比较长,一共三部分:
    1、连接数据库查询例子,无参数化
    //查询收文操作,通过数据库查询记录数是否正确
    //1、输出记录数值,例如78条  2、获取输出的记录数值  3、连接数据库,查询记录数
    4、输出记录数值和从数据库中查询记录数值,相比较,相等则成功,不等则失败
    Browser("湛江信息化测试登录").Page("湛江东兴石油企业有限公司办公自动化系统").Frame("mainFrame").Output CheckPoint("78")
    Dim mm
    'mm=DataTable.GlobalSheet.GetParameter("mainFrameOutput_Text_out").Value 
    //注释,获取datatable值与DataTable("mainFrameOutput_Text_out", dtGlobalSheet)一致
    mm=DataTable("mainFrameOutput_Text_out", dtGlobalSheet)
    MsgBox mm
    Dim res,cmd,sql
    Set res=createobject("adodb.recordset")
    Set cmd=createobject("adodb.command")
    Cmd.activec
    Cmd.CommandType = 1
    sql="select count(*) from oa_receivebumf  where BUMFNAME like '%收文测试%'"
    'sql="select  count(*)  from oa_receivebumf  where BUMFNAME='"&nn&"'"
    //注释,sql语句,等于时sql语句
    // sql="select  count(*)  from oa_receivebumf  where BUMFNAME like '%nn%'" //like时sql语句
    Cmd.CommandText = sql
    Set res = Cmd.Execute()
    //msgbox res("name")

    MsgBox res(0)
    If   Cstr(res(0)) = Cstr(mm)Then
          Reporter.ReportEvent micPass, "test",   "查询成功"    
      else
    Reporter.ReportEvent micfail, "test",   "查询失败"
    End If
    Set res = nothing
    Set cmd.ActiveConnection = nothing
    Set Cmd= nothing
    2、登记用户,查看是否登记成功
       //登记用户,查询用户是否存在在数据库中
         1、 参数化 2、取参数化值  3、查询语句中,赋值给查询条件
         4、从数据库中查询出用户名,与参数化中值做比较
       脚本如下:
    Browser("测试登录").Page("办公自动化系统").Frame("mainFrame_2").WebEdit("addressVO.name").Set DataTable("p_Text", dtGlobalSheet)
    Dim xname
    xname=DataTable("p_Text", dtGlobalSheet)
    MsgBox  xname

    Dim res,cmd,sql
    Set res=createobject("adodb.recordset")
    Set cmd=createobject("adodb.command")
    Cmd.activec
    Cmd.CommandType = 1
    sql="select  name from address_list t where name ='"&xname&"'"
    Cmd.CommandText = sql
    Set res = Cmd.Execute()
    'msgbox res("name")
    MsgBox res(0)
    Set res = nothing
    Set cmd.ActiveConnection = nothing
    Set Cmd= nothing

     

     

    第二部分

    1、Datatable方法GetRowCount
       DataTable.GetSheet("Action1").GetRowCount   //获取总行数
      使用如:
      CountNum=DataTable.GetSheet("Action1").GetRowCount

    2、Datatable方法SetNextRow
       DataTable.GetSheet("Action1").SetNextRow     //取得下一行
       datatable.setcurrentrow(n)   //取得某一行

    3、Datatable方法getcurrentrow    //获得当前行数
       例如:datatable.getcurrentrow

    4、获取datatable值
       4.1  DataTable("p_Text", dtLocalSheet)  //取得datatable中参数名称为:p_Text的值
       4.2  DataTable.GlobalSheet.GetParameter("p_Text").Value   //获取参数值方法和DataTable("p_Text", dtLocalSheet)一样
       例如:xname为变量,dim xname
       xname=DataTable("p_Text", dtLocalSheet)
       xname=DataTable.GlobalSheet.GetParameter("p_Text").Value

    5、datatable.value("num")只在global形式下的一种省略形式;完整形式是:
    datatable.value("num",dtlocalsheet)

    -----向某一列的单元格赋值:
    datatable.value("column_name",dtlocalsheet)="nanjing"

    6、字符转换Cstr
       dim mm
       Cstr(mm)

    7、获取对象属性名称用法:
    GetRoProperty----从应用程序界面上获取对象属性(即,是脚本运行时,获取的对象动态属性值)
               例如:获取对象库中index属性值,似乎只能用GetToProperty,因为应用程序界面上对象没有该属性,只是
          QTP为识别该对象创立的描述属性;
    GetToproperty----从对象库中描述对象的属性,静态值
    GetToProperties----获取用于标识对象的属性集;对于这个集合,有count等属性方法

    8、如果弹出对话框就获取上面提示信息并与表中的信息对比,不统一证明弹出的提示出错,主要用来验证
    if browser("web_name").dialog("dialog_name").exist(1) then'如果不出现=false
         error_message=browser("web_name").dialog("diaglog_name").static("用户密码错误!".getRoproperty("text")
       if error_message<>(datatable.value("error_info"))then
             msgbox(error_message)
          end if
         browser("web_name").dialog("diaglog_name").close
      end if
    这里总结了两点技巧:
      一是:对于dialog中,虽然提示信息对象名称是"用户密码错误",但如果信息对象名称是“该用户不存在”,不用更改会自动识别,我想主要是录制第一遍时,“用户密码错误”只是让运行时能找到这个控制,而不管它是什么内容,因为在对象仓库中,text不是决定该对象的属性
        二是:如果对于提示信息比较长的,可以用mid(error_message,n,m)取一部份特征提示信息进行验证,这样我想可以节省处理时间,又可以避免长度以及空格等字符的处理

    9、数据库检查点模块:
    sub database_check
    set con=createobject("adodb.connection")
    con.open "Description=IBM_ODBC;DRIVER=SQL Server;SERVER=IBM;UID=sa;"&_
                     "PWD=123456;APP=Quick Test Pro;WSID=IBM;DATABASE=IBM_table"
    'access方式:con.open "DRIVER={Microsoft Access Driver (*.mdb)};DBQ=d:\test.mdb"
    'Orocle方式:con.open "DRIVER={Oracle in OraHome92};SERVER=CESHI;UID=CND_TEST;PWD=CND;DBQ=CESHI;DBA=W;APA=T;EXC=F;XSM=Default;FEN=T;QTO=T;FRC=10;FDL=10;LOB=T;RST=T;GDE=F;FRL=Lo;BAM=IfAllSuccessful;MTS=F;MDI=Me;CSR=F;FWC=F;PFC=10;TLO=O;"
    set record=createobject("adodb.recordset")
    sql="select*from ibm_one_table"
    record.open sql,con
    DO
    if(record("ibm_table_column")="kai")then'//查找表格中有多少kai
    num=num+1;
    end if
    record.movenext
    loop until record.eof=true
    record.close
    set record=nothing
    con.close
    set con=nothing
    end sub

    10、"is+*"类型function
    isarray'是否是数组
    isconnected'判断QTP是否连接到TD
    isdate'是否是合法的日期类型
    isempty'判断是否初始化
    isNull'判断是否为空值
    isNumeric'判断是否是数字型
    isobject'判断是否一个功能对象
    isready'判断设备是否准备就绪
    isRootFolder'是否是根目录

    11、for方法1,参数化时选择:dtLocalSheet
    Dim CountNum
    CountNum=DataTable.GetSheet("Action1").GetRowCount
    For i=0 to CountNum-1
    ----xunhuanti------
    DataTable.GetSheet("Action1").SetNextRow    //使用SetNextRow方法
    Next

    12、for方法2,参数化时选择:dtLocalSheet
    dim countNum
    countNum = DataTable.GetSheet("Action1").GetRowCount
    For i=1 to countNum
    DataTable.GetSheet("Action1").SetCurrentRow(i)  //使用SetCurrentRow(i)方法
    ―――ddd―――
    next
    13、while方法1,参数化时选择:dtLocalSheet
    Dim CountNum,i
    i=1
    CountNum=DataTable.GetSheet("Action1").GetRowCount
    While i<=CountNum
    ------xuhuanti---
    DataTable.GetSheet("Action1").SetNextRow
    i = i+1
    Wend

    14、while方法2,参数化时选择:dtLocalSheet
    Dim CountNum,i
    i=1
    CountNum=DataTable.GetSheet("Action1").GetRowCount
    While i<=CountNum

    DataTable.GetSheet("Action1").SetCurrentRow(i)
    ----xuhuanti---
    i = i+1
    Wend

     

     

    第三部分

    15、Do while方法
    Dim i,RowCount '定义两个变量
    i=0
    RowCount=DataTable.GetSheet("Action1").GetRowCount '设置RowCount等于Action1中的行数。
    msgbox RowCount
    Do while i<rowcount
    i=i+1 '第一次进入循环,执行这句后,i=1
    'DataTable.GetSheet("Action1").SetCurrentRow(i)  这句话被我注释掉了,正确的写法应该是下面这样,分开写。

    datatable.getsheet("Action1")
    datatable.setcurrentrow(i)

    ----xunhuanti----
    loop

    16、取对象属性(Property)值

    Dim usname
    usname =Browser("测试登录").Page("办公自动化系统").Frame("mainFrame_2").WebEdit("addressVO.name").GetRoProperty("Value")   '获取对象属性(Property)值,如Property为Value
    MsgBox usname

    17、取得要删除的id,并删除
    'url在查看该新增记录的信息页面对象中取得,所以录制的时候,登记,查看(修改),删除

    Dim strUserid,id,strId

         id=Browser("测试登录").Page("办公自动化系统").Frame("mainFrame_4").GetROProperty("url") '在url这个属性值中存在我需要删除记录的ID信息
      strId=Mid (id,instr(60,id,"=")+1)  '这一步是把需要的id值取了出来,例如:strId=Mid (id,instr(1,id,"=")+1)
         strUserid =strId  'strUserid是我要删除的记录前的复选框属性值当中的ID信息
    Browser("测试登录").Page("办公自动化系统").Frame("mainFrame").WebCheckBox("value:="&strUserid).Set "ON" 这样就把想删除的记录选中了。
    Browser("测试登录").Page("办公自动化系统").Frame("mainFrame").WebButton("删 除").Click '这样就删除掉啦,呵呵

    17.2通过数据库取得id值,并赋值进行删除

    Browser("测试登录").Page("办公自动化系统").Frame("mainFrame_2").WebEdit("addressVO.name").Set DataTable("p_Text", dtGlobalSheet)
    Browser("测试登录").Page("办公自动化系统").Frame("mainFrame_2").WebEdit("addressVO.address").Set DataTable("p_Text1", dtGlobalSheet)

    Browser("测试登录").Page("办公自动化系统").Frame("mainFrame_2").WebEdit("addressVO.unitTel").Set DataTable("p_Text2", dtGlobalSheet)

    Dim xname,address,unitTel
    xname=DataTable("p_Text", dtGlobalSheet)   '从datatable中取值
    address=DataTable("p_Text1", dtGlobalSheet)
    unitTel=DataTable("p_Text2", dtGlobalSheet)

    Dim res,cmd,sql
    Set res=createobject("adodb.recordset")
    Set cmd=createobject("adodb.command")
    Cmd.activeconnection="DRIVER={Oracle in OraHome92};SERVER=HKORACLE;UID=USER22;PWD=ZJLH;DBQ=HKORACLE;DBA=W;APA=T;EXC=F;XSM=Default;FEN=T;QTO=T;FRC=10;FDL=10;LOB=T;RST=T;BTD=F;BAM=IfAllSuccessful;NUM=NLS;DPM=F;MTS=T;MDI=Me;CSR=F;FWC=F;FBS=60000;TLO=O;"
    Cmd.CommandType = 1
    'sql="select  addressid from address_list t where name ='"&xname&"'"
    'sql="select  addressid from address_list t where name ='"&xname&"'
    sql="select  addressid from address_list t where name ='"&xname&"' and address='"&address&"'  and unitTel='"&unitTel&"'"
    Cmd.CommandText = sql
    Set res = Cmd.Execute()

    MsgBox res(0)  '打印res(0)
    DataTable("addressid", dtGlobalSheet)=Cstr(res(0))   '输出值到datatable中

    Browser("测试登录").Page("办公自动化系统").Frame("mainFrame").WebCheckBox("value:="&res(0)).Set "ON"
    Set res = nothing
    Set cmd.ActiveConnection = nothing
    Set Cmd= nothing


    18

  • 只读和可读区别

    2010-12-17 15:25:00

    可读意味着该字段可以显示出来,只读有两种方式一种通过系统自动设置,一种将不允许编辑数据以此种方式显示
    只读字段要显示出来的,但又分几种情况,一种是只读属性应用id时,一般id不会显示, 第二种只读属性应用在其他值如(日期赋值,系统自动获取代维公司,用户信息,其他信息)可以显示,但在不同节点有不同的执行权限。
    但是有时候只读属性不会显示出来,比如input 标签 type="hidden"
    这个只是概念定义,主要还是为了应用项目中,产生不同操作之间的区别。
  • LR9.1录制web Tour自带

    2010-12-03 16:12:51

    测试脚本 :

     

    登录操作放在了初始化

    主要关联sessionid

     

    vuser_init()

    {

           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("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=t5.inf",

                  "Mode=HTTP",

                  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=t3.inf",

                  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("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=t6.inf",

                  "Mode=HTTP",

                  LAST);

     

           //读取登录用户相关信息,和后面的订票确认相关联

    //此过程通过切换树形目录下,读取server response获得

           web_reg_save_param("login",

                  "LB=userSession value=",

                  "RB=>",

                  "Ord=1",

                  "Search=Body",

                  "RelFrameId=1",

                  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=t7.inf",

                  "Mode=HTTP",

                  LAST);

     

           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=t8.inf",

                  LAST);

     

           lr_think_time(4);

    // 登录动作,这里把session进行关联

           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=t9.inf",

                  "Mode=HTTP",

                  ITEMDATA,

                  "Name=userSession", "Value={login}", ENDITEM,

                  "Name=username", "Value=jojo", ENDITEM,

                  "Name=password", "Value=bean", ENDITEM,

                  "Name=JSFormSubmit", "Value=off", ENDITEM,

                  "Name=login.x", "Value=0", ENDITEM,

                  "Name=login.y", "Value=0", ENDITEM,

                  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=t10.inf",

                  "Mode=HTTP",

                  LAST);

     

           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=t11.inf",

                  "Mode=HTTP",

                  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=t12.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=t13.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=t14.inf",

                  LAST);

     

           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("Search Flights Button",

                  "URL=http://127.0.0.1:1080/WebTours/welcome.pl?page=search",

                  "Resource=0",

                  "RecContentType=text/html",

                  "Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home",

                  "Snapshot=t16.inf",

                  "Mode=HTTP",

                  LAST);

     

           web_url("reservations.pl",

                  "URL=http://127.0.0.1:1080/WebTours/reservations.pl?page=welcome",

                  "Resource=0",

                  "RecContentType=text/html",

                  "Referer=http://127.0.0.1:1080/WebTours/welcome.pl?page=search",

                  "Snapshot=t17.inf",

                  "Mode=HTTP",

                  LAST);

     

           web_url("nav.pl_3",

                  "URL=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=flights",

                  "Resource=0",

                  "RecContentType=text/html",

                  "Referer=http://127.0.0.1:1080/WebTours/welcome.pl?page=search",

                  "Snapshot=t18.inf",

                  "Mode=HTTP",

                  LAST);

     

           web_url("in_flights.gif",

                  "URL=http://127.0.0.1:1080/WebTours/images/in_flights.gif",

                  "Resource=1",

                  "RecContentType=image/gif",

                  "Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=flights",

                  "Snapshot=t19.inf",

                  LAST);

     

           web_url("home.gif",

                  "URL=http://127.0.0.1:1080/WebTours/images/home.gif",

                  "Resource=1",

                  "RecContentType=image/gif",

                  "Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=flights",

                  "Snapshot=t21.inf",

                  LAST);

     

           web_url("button_next.gif",

                  "URL=http://127.0.0.1:1080/WebTours/images/button_next.gif",

                  "Resource=1",

                  "RecContentType=image/gif",

                  "Referer=http://127.0.0.1:1080/WebTours/reservations.pl?page=welcome",

                  "Snapshot=t20.inf",

                  LAST);

     

           web_url("FormDateUpdate.class",

                  "URL=http://127.0.0.1:1080/WebTours/FormDateUpdate.class",

                  "Resource=0",

                  "RecContentType=text/html",

                  "Referer=",

                  "Mode=HTTP",

                  LAST);

     

           web_url("CalSelect.class",

                  "URL=http://127.0.0.1:1080/WebTours/CalSelect.class",

                  "Resource=0",

                  "RecContentType=text/html",

                  "Referer=",

                  "Mode=HTTP",

                  LAST);

     

           web_url("Calendar.class",

                  "URL=http://127.0.0.1:1080/WebTours/Calendar.class",

                  "Resource=0",

                  "RecContentType=text/html",

                  "Referer=",

                  "Mode=HTTP",

                  LAST);

     

           return 0;

    }

    以下是Action,主要针对城市,生成对应的机票信息关联

     

    Action()

    {

           char fromCity[50];//记录出发城市

           char toCity[50];//记录到达城市

          

           lr_start_transaction("filght");

           lr_think_time(14);

           //条件判断,如果出发城市和目的城市不一样,进行订票

           strcpy( fromCity,lr__string("{testcp1}") );

           strcpy( toCity,lr__string("{testcp2}") );

          

           while(strcmp(fromCity,toCity)==0)

                  {

                         strcpy( toCity,lr__string("{testcp2}") );

                         lr_output_message("current testcp2=%s", toCity);

                         lr_output_message("current testcp1=%s", fromCity);

                  }

    //使用转换函数,将C语言的变量,存储为LR得变量

    //C变量tocity,存放与LR临时变量TargetCity中,

     

    lr_save_string(lr__string(toCity),"TargetCity");

     

    //C变量fromCity存放于LR临时变量TrivalCity中,

     

    lr_save_string(lr__string(fromCity),"TrivalCity");

     

           //关联单程机票相关信息,确认及支付过程使用

           web_reg_save_param("FLIGHT",

                  "LB=input type = radio name=outboundFlight value=",

                  "RB=checked >",

                  LAST);

           //关联返程机票相关信息,确认及支付过程使用

           web_reg_save_param("returnFli",

                  "LB=input type = radio name=returnFlight value=",

                  "RB=checked >",

                  LAST);

     

           web_submit_data("reservations.pl_2",

                  "Action=http://127.0.0.1:1080/WebTours/reservations.pl",

                  "Method=POST",

                  "RecContentType=text/html",

                  "Referer=http://127.0.0.1:1080/WebTours/reservations.pl?page=welcome",

                  "Snapshot=t22.inf",

                  "Mode=HTTP",

                  ITEMDATA,

                  "Name=advanceDiscount", "Value=0", ENDITEM,

                  "Name=depart", "Value={TrivalCity }", ENDITEM,

                  "Name=departDate", "Value=06/09/2009", ENDITEM,

                  "Name=arrive", "Value={TargetCity}", ENDITEM,

                  "Name=returnDate", "Value=06/10/2009", ENDITEM,

                  "Name=numPassengers", "Value=1", ENDITEM,

                  "Name=roundtrip", "Value=on", ENDITEM,

                  "Name=seatPref", "Value=None", ENDITEM,

                  "Name=seatType", "Value=Coach", ENDITEM,

                  "Name=.cgifields", "Value=roundtrip", ENDITEM,

                  "Name=.cgifields", "Value=seatType", ENDITEM,

                  "Name=.cgifields", "Value=seatPref", ENDITEM,

                  "Name=findFlights.x", "Value=63", ENDITEM,

                  "Name=findFlights.y", "Value=8", ENDITEM,

                  LAST);

     

    //根据城市的选择,生成对应的机票信息,因票价及航班为变化的,固此处必须关联

           web_submit_data("reservations.pl_3",

                  "Action=http://127.0.0.1:1080/WebTours/reservations.pl",

                  "Method=POST",

                  "RecContentType=text/html",

                  "Referer=http://127.0.0.1:1080/WebTours/reservations.pl",

                  "Snapshot=t23.inf",

                  "Mode=HTTP",

                  ITEMDATA,

                  "Name=outboundFlight", "Value={FLIGHT}", ENDITEM,

                  "Name=returnFlight", "Value={returnFli}", ENDITEM,

                  "Name=numPassengers", "Value=1", ENDITEM,

                  "Name=advanceDiscount", "Value=0", ENDITEM,

                  "Name=seatType", "Value=Coach", ENDITEM,

                  "Name=seatPref", "Value=None", ENDITEM,

                  "Name=reserveFlights.x", "Value=36", ENDITEM,

                  "Name=reserveFlights.y", "Value=11", ENDITEM,

                  LAST);

     

           //此步骤为用户身份确认,来自登录客户的session中,前面如果session失效,测试结果显示通过,实际未对服务器产生压力

           //判断: 来自执行结果中的机票信息

           //脚本中,还可以发现生成的对应的关联机票信息

           web_submit_data("reservations.pl_4",

                  "Action=http://127.0.0.1:1080/WebTours/reservations.pl",

                  "Method=POST",

                  "RecContentType=text/html",

                  "Referer=http://127.0.0.1:1080/WebTours/reservations.pl",

                  "Snapshot=t24.inf",

                  "Mode=HTTP",

                  ITEMDATA,

                  "Name=firstName", "Value=Joseph", ENDITEM,

                  "Name=lastName", "Value=Marshall", ENDITEM,

                  "Name=address1", "Value=234 Willow Drive", ENDITEM,

                  "Name=address2", "Value=San Jose/CA/94085", ENDITEM,

                  "Name=pass1", "Value=Joseph Marshall", ENDITEM,

                  "Name=creditCard", "Value=", ENDITEM,

                  "Name=expDate", "Value=", ENDITEM,

                  "Name=oldCCOption", "Value=", ENDITEM,

                  "Name=numPassengers", "Value=1", ENDITEM,

                  "Name=seatType", "Value=Coach", ENDITEM,

                  "Name=seatPref", "Value=None", ENDITEM,

                  "Name=outboundFlight", "Value={FLIGHT}", ENDITEM,

                  "Name=advanceDiscount", "Value=0", ENDITEM,

                  "Name=returnFlight", "Value={returnFli}", ENDITEM,

                  "Name=JSFormSubmit", "Value=off", ENDITEM,

                  "Name=.cgifields", "Value=saveCC", ENDITEM,

                  "Name=buyFlights.x", "Value=40", ENDITEM,

                  "Name=buyFlights.y", "Value=10", ENDITEM,

                  LAST);

           web_url("bookanother.gif",

                  "URL=http://127.0.0.1:1080/WebTours/images/bookanother.gif",

                  "Resource=1",

                  "RecContentType=image/gif",

                  "Referer=http://127.0.0.1:1080/WebTours/reservations.pl",

                  "Snapshot=t25.inf",

                  LAST);

     

           lr_end_transaction("filght", LR_AUTO);

     

           return 0;

    }

     

    为什么要关联:

    所谓的关联(correlation)就是把脚本中某些写死的(hard-coded)数据,转变成是撷取自服务器所送的、动态的、每次都不一样的数据。

    通过关联,上下文中变化的信息,其值的获取来自服务器,而不在是生成脚本中的固定值

    即:参数变化的同时,服务器的返回值通过关联函数,被调用。

    关联的取值,通过左右边界,

    web_reg_save_param函数主要是透过动态数据的前面和后面的固定字符串,来辨识要撷取的动态数据的,所以我们还需要找出动态数据的边界字符串。

    例子:

    web_reg_save_param("outFlightVal",

                  "LB=outboundFlight value=", "RB=>",

                   "ORD=ALL",

                   "SaveLen=18",

                 LAST);

    ORD 用来确定第几次出现的值,是要关联的值 ,默认值为1,就是第一次出现的值为关联的值,而设置成ALL则表示关联全部存在于左右边界的值。

    SaveLen用来截取获得的结果,如果结果中,存在我们不需要的信息,使用SaveLen来获取,例子中,只去的18个字节的串,后面的内容就被删减掉。 

  • LR脚本练习3

    2010-12-03 09:46:08

    1、写入数据到文件:(实际应用中可以将关联得到的参数写入文件)
     
    1. Action()   
    2. {   
    3. int MyFile;   
    4. char Name[] = "测试数据";  MyFile = fopen( "c:\\temp\\names.txt",  "w+"  );   
    5. fprintf(MyFile,"%s", Name);   
    6. return 0;     
    7. }  

    2、atol类型转换字符串转成整型(atoi  atof  itoa)
     Action()  
    1. {   
    2. char a[512];   
    3. lr_output_message("value:%s",lr_eval_string("{param1}"));   
    4. sprintf(a,"value=%ld",atol(lr_eval_string("{param1}"))+1);   
    5. lr_output_message("value:%s",a);    
    6. return 0;   
    7. }  

    3、 fopen():返回一个FILE数据类型的指针.因为LoadRunner不支持FILE数据类型,所以返回值需要转化成int型.
          int MyFile;
         MyFile=(int)fopen("C:\\temp\\loans.txt","w");
    fopen()函数的第一个参数是创建文件的路径.第二个参数指定了创建文件的模式.下面是常用的几种模式:
    “w” - 写,当需要往文件中写的时候.如果文件存在,就覆盖该文件,如果文件不存在,根据第一个参数来创建新文件.
    “r” 读,需要从文件中读的时候.这个文件必须已经存在.
    “a” 附加,当往文件末尾添加数据时用到.
    “rw” 读和写.
    第一个参数中注意文件路径为"\\",因为"\"在C语言中为转义字符.另外,如果文件和脚本在同一个目录中,文件的完整路径可以省略.
    1. Action() {   
    2. int count, total = 0;   
    3. char buffer[1000];   
    4. long file_stream;   
    5. char *filename = "c:\\readme.txt";   
    6. if ((file_stream = fopen(filename, "r")) == NULL ) {   
    7. lr_error_message("Cannot open %s", filename);   
    8. return -1;   
    9. }   
    10. while (!feof(file_stream)) {   
    11. count = fread(buffer, sizeof(char), 1000, file_stream);   
    12. lr_output_message("%3d read", count);   
    13. if (ferror(file_stream)) {   
    14. lr_output_message("Error reading file %s", filename);   
    15. break;   
    16. }   
    17. total += count;    
    18. }   
    19. lr_output_message("Total number of bytes read = %d", total );   
    20. if (fclose(file_stream))   
    21. lr_error_message("Error closing file %s", filename);   
    22. return 0;   
    23. }  

    4、lr_save_string
    (将非空字符串保存到指定的参数中,可将关联景中处理过的字符保存起来,以便后面进行参数化。)

    5、lr_eval_string (用于返回参数中的实际字符串值,可以使用该函数来查看参数化取值是否正确。)
    如:lr_output_message(“ID is %s” , lr_eval_string(“{id}”));
    补充:web_url()函数详解
      web_url()函数可以模拟用户请求,它也是在脚本中最常使用的函数之一。
      web_url()函数的基本语法如下所示:
      web_url("在测试结果中显示的名称","URL=需要访问的超链地址",LAST);

      和web_link不同的地方在于这里只需要在URL=后填写需要访问的地址即可,和在IE地址栏中输入的内容相同,使用web_url的好处是没有任何请求的前后依赖关系,只负责发送一个标准的Get HTTP请求。
      如果需要访问51Testing论坛,可以直接这样写:
      1. web_url("51testing","URL=http://bbs.51testing.com",LAST);
      除了以上这些元素,在录制出来的web_link或者web_url函数中经常还能看到如下所示的大量内容:
      1. EXTRARES
      2. "Url=../bite.jpg", "Referer=http://192.168.0.200", ENDITEM,
      3. "Url=../title.gif", "Referer=http://192.168.0.200", ENDITEM,
      4. ……
      这一段内容说明在载入这个页面时还有其他图片或者附属资源需要下载。
      web_link()和web_url()函数都是页面访问型函数,实现HTTP请求中的GET方法,如果需要提交表单,实现HTTP请求中的POST方法,那么需要使用web_submit_form()或web_submit_data()函数。
    补充:web_submit_form()函数详解
      该函数会自动检测在当前页面上是否存在form,然后将后面的ITEMDATA数据进行传送。例如录制在Web Tours网站上登录操作,可以得到以下代码:
      1. web_submit_form("login.pl",
      2.     "Snapshot=t3.inf",
      3.     ITEMDATA,
      4.     "Name=username", "Value=admin", ENDITEM,
      5.     "Name=password", "Value=123456", ENDITEM,
      6.     "Name=login.x", "Value=0", ENDITEM,
      7.     "Name=login.y", "Value=0", ENDITEM,
      8.     LAST);
      隐藏的表单数据系统会自行发送。
      补充:web_submit_data()函数详解
      和web_submit_form()函数不同,web_submit_data()函数无须前面的页面支持,直接发送给对应页面相关数据即可。录制Web Tours网站登录,代码会变为:
      1. web_submit_data("login.pl",
      2.     "Action=http://127.0.0.1:1080/WebTours/login.pl",
      3.     "Method=POST",
      4.     "TargetFrame=body",
      5.     "RecContentType=text/html",
      6.     "Referer=http://127.0.0.1:1080/WebTours/nav.pl?in=home",
      7.     "Snapshot=t5.inf",
      8.     "Mode=HTML",
      9.     ITEMDATA,
      10.     "Name=userSession", "Value=100084.208748481fVtiAiVptiHfDAiiiptiiQcf", ENDITEM,
      11.     "Name=username", "Value=admin", ENDITEM,
      12.     "Name=password", "Value=123456", ENDITEM,
      13.     "Name=JSFormSubmit", "Value=off", ENDITEM,
      14.     "Name=login.x", "Value=0", ENDITEM,
      15.     "Name=login.y", "Value=0", ENDITEM,
      16.     LAST);
      其中Action说明提交表单的处理页面,Method表明提交数据的方式。
      当使用web_submit_data()函数时,隐藏表单的数据也会被记录下来作为ITEMDATA数据提交给服务器。  这里介绍了在HTTP页面中最常用的4个页面函数web_link()、web_url()、web_submit_form()、 web_submit_data(),通过这些函数可以实现大多数页面访问的请求和数据提交的过程。除了这4个函数,还有一个函数可能会经常看到:web_custom_request()。当请求比较特别时,VuGen无法简单使用以上4个函数进行表述,那么录制后便会出现 web_custom_request()函数,这个函数的作用是自定义HTTP请求规则。该函数更适合在使用自定义的HTTP请求规则中处理二进制内容。具体格式请参考帮助文档。
    补充:web_link()函数详解
      web_link()函数用来模拟用户单击一个超链接的操作。VuGen会记录访问页面后服务器返回的内容中有多少个超链接。当使用web_link()函数时,只要写出正确的链接名,VuGen会自动查找并访问页面中该链接名所指向的URL地址。
      web_link()函数的基本语法如下所示:
      web_link(“在测试结果中显示的名称”,“TEXT=需要单击的超链接名”,LAST);
      ◇ 在测试结果中显示的名称
      也被称作步骤名,在测试结果中显示的名称是指在脚本运行完成后,打开Test Result,在link函数后的名称(此处为sign up now),如图3.16所示。

    图3.16  Test Result执行步骤
      这是通过web_link(“sign up now”,….)来实现的,我们能够在测试结果中方便、快速地定位。
      ◇ 需要单击的超链接名
    单击的链接是通过Text=来说明的,等号后的内容就是需要单击的链接。这里需要注意,如果Text后的链接名不存在,那么就会得到以下错误: 该错误信息提示单击的signupnow这个链接不存在,整个web_link函数是错误的。
    “Snapshot=t2.inf”用来说明该操作后的内容会被抓图保存到文件t2.inf中。最后LAST表明这个函数的结束。
    例如:想要该脚本去点击WebTours首页上的administration链接,我们只需要将web_link修改为: 回放脚本看看是否正确通过,并进入管理页面。
      思考:
      如果一个页面中有多个同名的链接,使用web_link()该如何处理?
      Ord这个关键字可以帮助你,在VuGen中很多函数都使用这个参数来判断对象的次序。
      例如要单击页面上的第二个链接,那么可以这样写:
      在这里需要注意,同名链接的先后顺序是根据HTML代码的解释顺序(从左往右,从上往下)来确定的。使用HTML-base script下的A script. describing user actions好处是脚本简洁,基于用户操作进行模拟,浅显易懂,并且自身就包含了对象检查过程,无须校验。其缺点是当页面中存在多个同名链接时难以区分。所以我们建议使用下面一种脚本模式:A script. containing  explicit URLs only (e.g. web_url,web_submit_data)。
    8、LoadRunner如何设置文本和网页图像的检查点。
    通过 VuGen 可在网页上添加搜索文本字符串的检查。可以在录制期间或录制之后添加文本检查。
    在创建文本检查时,需要定义检查的名称、检查范围、要检查的文本和搜索条件。
    要在录制之后添加文本检查,请执行下列操作:
      1.在 VuGen 主窗口中,右键单击与要对其执行检查的网页相应的步骤。从弹出菜单中选择“在之后插入”。将打开“添加步骤”对话框。
      2.在“步骤类型”树中,展开“Web 检查”。
      3.选择“文本检查”,然后单击“确定”。将打开“文本检查属性”对话框。请确保“规格”选项卡可见。
      4.在“搜索”框中,键入要验证其存在与否的字符串。ABC 图标表示尚未为“搜索”框中的字符串分配参数。
      5.要相对于邻近文本指定搜索字符串的位置,请选中“其右侧”或“其左侧”复选框。然后,在适当的字段中键入文本。例如,要验证字符串support@mercuryinteractive.com是否出现在单词“e-mail:”的右侧,请选中“其右侧”,然后在“其右侧”框中键入“e-mail:”。ABC 图标表示尚未为“其右侧”或“其左侧”框中的字符串分配参数。
      6.命名文本检查。单击“常规”选项卡,然后在“步骤名”框中键入文本检查的名称。使用一个以后容易识别该检查的名称。
      7. 属性表显示其他用于定义检查的属性。清除“仅查看活动属性”复选框可以查看活动和非活动属性。要启用某个属性,请单击该属性名左侧的单元格。在“值”列中为属性分配一个值。
    8.单击“确定”接受设置。代表新文本检查的图标将被添加到脚本中的关联步骤中。在脚本视图中,“文本检查”图标显示为 web_find 函数。要在录制期间添加文本检查,请执行下列操作:
      1.使用鼠标标记所需的文本。
      2.单击录制工具栏上的“插入文本检查”图标。
      除了使用 web_find 函数外,还可以使用两个其他的增强函数来搜索 HTML 页内的文本:
      web_reg_find
      web_global_verification
      web_reg_find 函数是注册类型函数。它将注册对 HTML 页上的文本字符串进行的搜索。注册意味着它不会立即执行搜索 - 仅在执行下一个操作函数(如web_url)之后,才会执行检查。注意,如果正在使用并发函数组,则web_reg_find 函数仅在分组结束后才会执行。该函数与 web_find 函数的不同之处在于:它并不局限于基于 HTML 的脚本(请参见“录制选项” > “录制”选项卡)。该函数还具有其他属性(如实例)通过该属性可以确定文本出现的次数。在执行标准文本搜索时, web_reg_find是首选函数。通过 VuGen 可添加在网页上搜索图像的用户定义的检查。图像可以由 ALT 属性、SRC 属性或这两者来标识。可以在录制期间或录制之后添加用户定义的图像检查。录制之后,可以在脚本中编辑任何现有的图像检查。要添加图像检查,请执行下列操作:
      1.在 VuGen 主窗口中,右键单击与要对其执行检查的网页相应的步骤。从弹出菜单中选择“在之后插入”。将打开“添加步骤”对话框。
      2.在“步骤类型”树中,展开“Web 检查”。
      3.选择“图像检查”,然后单击“确定”。将打开“图像检查属性”对话框。请确保“规格”选项卡可见。
      4.选择一种标识图像的方法:
      a)要使用图像的 ALT 属性来标识图像,请选中“替换图像名(ALT 属性)”复选框,然后键入 ALT 属性。在运行脚本时, Vuser 将搜索具有指定的 ALT 属性的图像。
      b)要使用图像的 SRC 属性来标识图像,请选中“图像服务器文件名(SRC 属性)”复选框,然后键入 SRC 属性。在运行脚本时, Vuser 将搜索具有指定的 SRC属性的图像。ABC 图标表示尚未为 ALT 或 SRC 属性分配参数。
      5.要命名图像检查,请单击“常规”选项卡。在“步骤名”框中,键入图像检查的名称。使用一个以后容易识别该检查的名称。
      6.属性表显示其他用于定义检查的属性。清除“仅查看活动属性”复选框可以查看活动和非活动属性。要启用某个属性,请单击该属性名左侧的单元格。在“值”列中为属性分配一个值。
      7.单击“确定”以接受设置。代表新图像检查的图标将被添加到 Vuser 脚本中的关联步骤中。可以指定插入到 Vuser 脚本中的每个 Web 检查的其他属性。在检查属性对话框的“常规”选项卡上的属性表中设置其他属性。
  • LR脚本练习2

    2010-12-03 09:45:25

    1、循环读取并显示该文件中的每行数据:(只能读数字内容)
     
    1. Action(){   
    2.      int MyFile;    
    3.      int loadNum,i;   
    4.       // Assigning the file path to a string   
    5.      char FileName[80] ="C:\\temp\\solem.txt";      
    6.      // Opening the file   
    7.      // Note the use of variable to replace the file path   
    8.     MyFile = (int)fopen(FileName,"r");     
    9.      while ( feof(MyFile)==0) {   
    10.      fscanf(MyFile,"%d",&loadNum);   
    11.      lr_output_message("LoadNum----------------> :%d \n", loadNum);   
    12.      }   
    13.       return 0;   
    14. }  

    2、循环读取并显示该文件中的每行数据:
     
    1. Action() {   
    2. char line[100] ;   
    3. long file_stream;   
    4. char *filename = "C:\\temp\\solem.txt";   
    5. if ((file_stream = fopen(filename, "r")) == null ) {      
    6.  lr_error_message("Cannot open %s", filename);   
    7. return -1;   
    8.   }   
    9.    while ( fgets(line, 100, file_stream)!= null) {   
    10.    lr_output_message( "The line is \"%s\"", line);   
    11. }   
    12. if (fclose(file_stream))   
    13.  lr_error_message("Error closing file %s", filename);   
    14. return 0;   
  • rand函数和srand函数

    2010-12-03 09:44:53

    #include <stdlib.h>

    #include <stdio.h>

    #include <time.h>

    int main(void)

    {

       int i;

       time_t t;

       srand((unsigned) time(&t));

       printf("Random numbers from 0 to 99\n");

       for(i=0; i<5; i++)

           printf("%d ", rand() % 100);

       return 0;

    }
    srand((unsigned) time(&t)); 
    产生随机种子(以系统时间来产生)。
    如果没有这一句,rand()出来的老是同一个数 .
     
    例子:函数 rand()随机产生90~100中6个数?
    初始化种子用的,要不然每次运行程序的时候,产生的6个随机数都是一样的。 
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    int main()
    {
     int i;
     time_t t;
     srand((unsigned) time(&t)); 
     for (i=0;i<6;i++)
     printf("%d\t%d\n",i,rand()%10+90);
     getch();
     
    } 
  • LoadRunner中的一个关联技巧

    2010-12-03 09:41:41

    众所周知,在LoadRunner中,关联是一个很重要的动作,大多数的脚本在录制完成后并不能直接回放,需要通过一定的关联才能成功回放。关联的技巧有很多,这里介绍的就是其中之一,以下用一个实际的例子来说明。
    5qM#LC,he$?;{'m;n0    脚本的背景如下:51Testing软件测试网3z!t kK,Y'K/D
        web_submit_data("classiLoanMaterial.jsf_2")(web_submit_data函数其它部分省略,下同。)返回的页面上可能存在多条记录,可能一条,可能两条,也可能三条,等等。我们需要将这些记录逐个选中进行操作。注意:不是全部选中,而是要逐条记录进行操作。同时,每一条记录各有一个编号,这是需要进行关联的值。在下面的操作中web_url("directAdjust.jsf",51Testing软件测试网|6[8| l,cx
            "URL=http://128.64.96.105:1158/clpmapp/bizprocess/loanservice/creditassetsriskclassi/
    5zF#B Lx T7[N0classiadjuststepbystep/directAdjust.jsf?approveFormNum=123456")需要使用到该编号,即黑体字部分的值。面对这样的目的,很自然地,我们会想到用一个循环语句来实现。首先,在classiLoanMaterial.jsf页面之前加一个关联如下:
    "n"nu^8RkRp:Z0    web_reg_save_param("sor","LB=sor\" value=\"","RB=\"","Ord=ALL", LAST);将Ord参数值设定为ALL,则关联函数将自动把符合条件的关联值保存到参数数组里。在本例中,假设关联值返回三条记录,则LR分别将值保存到sor_1,sor_2,sor_3中,同时,LR还将自动创建一个sor_count变量来保存总的记录数,在这里sor_count值等于3。利用这些信息,我们就可以很方便地在循环语句中实现我们的目的了。步骤如下:
    7@z pr0xj01、声明各变量:
    %A:i'?[OF3D0    int count;
    @&X"B,}s0    int i;
    "o9_RM.{Gx0    char sor[50];51Testing软件测试网[-J+f%{O!C kHV3I
        char sorvalue[50];51Testing软件测试网|*c2\9Z+N;GSl5FJ@N
    2、将返回的记录数保存到count变量里:
    1t:MP N'RY0count=atoi(lr_eval_string("{sor_count}"));
    /j,`9c|}\;oE03、使用for循环:51Testing软件测试网 q @*F V4L&K YO"l U3~
    for(i=1;i<=count;i++)
    8~xv*f6C,t;T0{
    $c4DN4Gy0    sprintf(sor,"{sor_%d}",i); //分别将各个sor值保存到sor字符串中
    |rY p Mj!V051Testing软件测试网 WYb\ iTZj
        sprintf(sorvalue,"%s",lr_eval_string(sor));//通过lr_eval_string函数将字符串赋给sorvalue变量51Testing软件测试网2Q m A:H @G I
    51Testing软件测试网#b ~g)c+xg-c,l~
    }51Testing软件测试网PiQ/dVlK
    4、在循环体中使用关联值替换相关值:
    hFh_A8m!x7A0web_url("directAdjust.jsf",51Testing软件测试网L9j t` K"Ibq/r
            "URL=http://128.64.96.105:1158/clpmapp/bizprocess/loanservice/creditassetsriskclassi/51Testing软件测试网}2u%qg'W
    classiadjuststepbystep/directAdjust.jsf?approveFormNum={sorvalue}")51Testing软件测试网u#S U^Cw$|
    51Testing软件测试网-PM-f'Tb(mnbpo/D&X
        一切看起来似乎顺理成章,然而如果按照以上的步骤做下来,将会很遗憾地发现:我们定义的{sorvalue}值根本就不被LR认可并接受,于是它将无情地给我们抛出一个错误,说该值是非法的。怎么办?难道我们前面做的一切都白费了吗?
    Jc*mD!lV*}n0    有句老话说得好:天无绝人之路。聪明而又善良的LR开发团队已经为我们考虑到了这个问题,给我们预备了一个很有用的函数:lr_save_string,它可以帮助我们解决这个问题。于是我们祭出lr_save_string这道最后的杀手锏:
    {g)z2DI05、在使用关联值之前进行字符串格式转换:51Testing软件测试网 jaf:SYI;[P
        lr_save_string(sorvalue,"sorvalue1");
    (Vj-g{pV f0   51Testing软件测试网6G(u I8Y2{8a"Wa
    web_url("directAdjust.jsf",
    'V'[,],M7f^,v5Z:_0u0        "URL=http://128.64.96.105:1158/clpmapp/bizprocess/loanservice/creditassetsriskclassi/
    2H+wmt:d(|2E Vj!XR0classiadjuststepbystep/directAdjust.jsf?approveFormNum={sorvalue1}")
    mAr+C\%MFr0
    B5TSh/D9Sw0需要特别注意lr_save_string的用法,它是参数值在前(sorvalue),参数名在后("sorvalue1"),这和一般的习惯用法正好反过来(真是好奇怪!)。而且"sorvalue1"这个参数名称不需要事先声明,它只是一个字符串而已(这也比较奇怪!^_^)。51Testing软件测试网&J}gXv'?m

    M5o k}.\7b,x0到此,我们总算大功告成!脚本回放成功,并且正确达到了预期的效果!打完收工!51Testing软件测试网:@#MT)mQ-N
    51Testing软件测试网ES4e5Ae@.y&b eWl
    总结:C的变量不能直接在LR的API里调用,所以必须用lr_save_string进行转换。51Testing软件测试网x8mP jZ`vw2U5U

    \"rq4S)o+M#H0最后顺便说一下,lr_save_string这个函数真的很好用,这个例子中提到的方法也适用于另外一些情况,比如说有时候,通过关联函数出来的值我们不能直接使用,还需要做一些特殊的处理时,那么我们可以把关联得到的值取出来,赋给一个字符串,对其进行一番修剪加工后,再用 lr_save_string,就可以使用它来替代需要关联的值了。
    0sJ B6K"us Mx051Testing软件测试网+|b'D!A$_0v^
    后记:我的这篇文章发布在网上以后,在广大的测试同行中间引起了强烈的反响,他们纷纷发来贺电和表扬信,对我这种勇于探索、乐于分享的精神给予了充分的肯定。^_^当然,这中间也难免存在极个别的不和谐声音,例如Zee同学就对我的这篇文章提出了不同看法,他觉得我的做法是把简单的问题复杂化了,理由是可以只做一次关联,每次只取第一笔记录即可,当循环进行操作时,第一笔做完以后,第二笔记录自然会上升到第一笔记录的位置,因此没有必要使用关联数组。我认为他的疑问并非没有道理,而且是比较有代表性的,因此我在这里做一个补充说明。在我接触过的大多数应用系统中,确实都是按照Zee所说的方式进行处理,在这种情况下,脚本的处理的确没有必要像我以上所述的那样复杂。不过我在本例中谈到的例子比较特殊,在操作完成后,它只是把每笔记录的状态位由“未完成”修改为“已完成”,而原有的记录并没有消失,而是仍然停留在原有的位置,此时如果按照Zee所说的方法,那么在执行第二次循环时,LR将取到操作状态为“已完成”的第一笔记录,而不会取到下一笔未完成的记录,显然这是不符合我们的要求的,因此在这里我需要做以上这样复杂的一个处理。
  • LR关联-随机删除一行数据和全部删除数据

    2010-12-02 14:11:36

    Lr学习之关联-随机删除一行数据和全部删除数据

    录制一个系统,我录制的是一个交通方面的系统,登陆到系统里面,查询车牌颜色,将其中一条数据删除。

    "Name=__EVENTTARGET", "Value=", ENDITEM,
    "Name=__EVENTARGUMENT", "Value=", ENDITEM,
    "Name=__VIEWSTATEENCRYPTED", "Value=", ENDITEM,
    "Name=__EVENTVALIDATION", "Value=d2nbwaXrFyhlu1wyuoxmLSw/21evncwEK/tTQwG6sFvqP2VtFAs+zeX0
    PXJIPIfG6XlFZhPOEA0kUeOT5pV8wqhGVVco5NpzdOHJ/au2SCODPGzuhOSC6N0
    XuUcOmmg9YQIQVRUjX9UN52yk1NWirekaG5UwowIEpykBMTVloq0Zy/IoAXnpG0
    NZmR1PB515k8ht2ALYsrgnpfervGZ84A==", ENDITEM,
    "Name=txtNumberColorName", "Value=", ENDITEM,
    "Name=gvItems$ctl04$ImageButton3.x", "Value=4", ENDITEM,
    "Name=gvItems$ctl04$ImageButton3.y", "Value=9", ENDITEM,
    LAST);

    以上是用来控制删除的代码。在上面代码里面最后两句是用来控制我删除的是第几行,看到数据104说明我删除的是第4行,也就是说如果我要删除第7行,我可以将代码自己改成:

    "Name=gvItems$ctl07$ImageButton3.x", "Value=4", ENDITEM,
    "Name=gvItems$ctl07$ImageButton3.y", "Value=9", ENDITEM,
    LAST);

    再运行一遍代码即可。我想让系统删除所有的记录,可以手动删除101,102,103。。。。。。。。。。,我们很容易想到循环。但是我们并不知道一共有多少行,这里我们需要关联可以知道一共有多少行,是哪些行,现在需要关心的是我们要关联哪个部分。

    我们将视图切换到树型目录下,找到删除记录的页面,点击Server Response选项卡,看到代码发现:

    <tr>
        <td>
            <span title="0024" style="cursor: pointer;">0024</span>
        </td>
        <td>
            <span title="8" style="cursor: pointer;">8</span>
        </td>
        <td>
            <span title="8" style="cursor: pointer;">8</span>
        </td>
        <td>
            <input type="image" name="gvItems$ctl02$ImageButton1" id="gvItems_ctl02_ImageButton1"
                text="缂栬緫" src="http://images.cnblogs.com/tableimage/bj.gif" alt="缂栬緫" style="border-width: 0px;" />
            <input type="image" name="gvItems$ctl02$ImageButton3" id="gvItems_ctl02_ImageButton3"
                src="http://images.cnblogs.com/tableimage/sc.gif" alt="鍒犻櫎" nclick="return confirm('纭疄瑕佸垹闄ゅ悧锛?);"
                style="border-width: 0px;" />
        </td>
    </tr>
    <tr>
        <td>
            <span title="0025" style="cursor: pointer;">0025</span>
        </td>
        <td>
            <span title="red" style="cursor: pointer;">red</span>
        </td>
        <td>
            <span title="1" style="cursor: pointer;">1</span>
        </td>
        <td>
            <input type="image" name="gvItems$ctl03$ImageButton1" id="gvItems_ctl03_ImageButton1"
                text="缂栬緫" src="http://images.cnblogs.com/tableimage/bj.gif" alt="缂栬緫" style="border-width: 0px;" />
            <input type="image" name="gvItems$ctl03$ImageButton3" id="gvItems_ctl03_ImageButton3"
                src="http://images.cnblogs.com/tableimage/sc.gif" alt="鍒犻櫎" nclick="return confirm('纭疄瑕佸垹闄ゅ悧锛?);"
                style="border-width: 0px;" />
        </td>
    </tr>

    行是由这样的代码一个个组成,每一行都有个唯一标识id="gvItems_ctl03_ImageButton3",那我们需要关联的是ID里面的那个数字。在代码上加:
    view source
    print?
    web_reg_save_param("idParam","LB=gvItems_ct","RB=_ImageButton3","ORD=All","Search=Body",LAST);

    然后添加显示的代码:

    i=atoi( lr_eval_string("{idParam_count}"));
    lr_error_message("%d",i);

    idParam_count变量装的就是一共有多少行。现在要做的就是找到这些行的唯一标识。
    view source
    print?
    for(j=1;j<=i;j++)
    {
        sprintf(temp,lr_eval_string("{idParam_%d}"),j);
        lr_error_message("the {idParam_%d} is %s",j,lr_eval_string(temp));
    }
    lr_error_message("%d",i);

    通过代码得到的结果为:

    Action.c(92): Notify: Saving Parameter "idParam_1 = l02"
    Action.c(92): Notify: Saving Parameter "idParam_2 = l03"
    Action.c(92): Notify: Saving Parameter "idParam_3 = l04"
    Action.c(92): Notify: Saving Parameter "idParam_4 = l05"
    Action.c(92): Notify: Saving Parameter "idParam_5 = l06"
    Action.c(92): Notify: Saving Parameter "idParam_count = 5"

     那么现在就好做全面删除了:
    view source
    print?
    for(j=1;j<=i;j++)
    {
        sprintf(temp,lr_eval_string("{idParam_%d}"),j);
        lr_error_message("%s",lr_eval_string("{temp}"));
        strcat(tempX,temp);
        strcat(tempX,"$ImageButton3.x");
        strcat(tempY,temp);
        strcat(tempY,"$ImageButton3.y");
        lr_error_message(tempX);
        lr_error_message(tempY);
        …………………//省略
        "Name=__EVENTVALIDATION", "Value={WCSParam_Diff2}", ENDITEM,
        "Name=txtNumberColorName", "Value=", ENDITEM,
        tempX, "Value=5", ENDITEM,
        tempY, "Value=5", ENDITEM,
        LAST);
    }

    若有疑问或不正之处,欢迎提出指正和讨论。

  • LR订票系统session关联

    2010-12-02 14:10:35

    以下是一个很典型的关联的例子:

    我们先录制系统自带的订票系统的登陆和退出的代码:

    Action()
    {
     
        web_url("WebTours",
            "URL=http://127.0.0.1:1080/WebTours/",
            "Resource=0",
            "RecContentType=text/html",
            "Referer=",
            "Snapshot=t1.inf",
            "Mode=HTML",
            LAST);
     
        lr_think_time(4);
     
        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=t2.inf",
            "Mode=HTML",
            ITEMDATA,
            "Name=userSession", "Value=104241.004374241fDzViQzpzzcfDcVHVpDHQQHf", ENDITEM, /*session号(随机的)*/
            "Name=username", "Value=test1", ENDITEM,
            "Name=password", "Value=test1", ENDITEM,
            "Name=JSFormSubmit", "Value=on", ENDITEM,
            "Name=login.x", "Value=48", ENDITEM,
            "Name=login.y", "Value=10", ENDITEM,
            LAST);
     
        web_image("Search Flights Button",
            "Alt=Search Flights Button",
            "Snapshot=t3.inf",
            LAST);
     
        web_image("SignOff Button",
            "Alt=SignOff Button",
            "Ordinal=1",
            "Snapshot=t4.inf",
            LAST);
     
        return 0;
    }

    录制完后,我们再次运行代码,报错:

    Action.c(31): Error -27987: Requested image not found    [MsgId: MERR-27987]

    Action.c(31): web_image("Search Flights Button") highest severity level was "ERROR", 0 body bytes, 0 header bytes  [MsgId: MMSG-26388]

    这里报错是因为每次登陆时系统为其分配一个session号(随机的)(红色标注的代码),所以要对seesion做关联。

    我们可以验证下:

    将刚刚录制的代码保存,再录制一遍刚才的步骤,点击工具——》对比代码:

    可以看到有三处不一样,第一处是userSession,后面是点击按钮时的x,y坐标。
    1:自动关联:
    a.点击Vuser->Scan Script. for Correlations点击对比后的Correlate即可。
    b. 将Vuser->Run-Time Setting的日志设置设置成扩展日志,在回放日志里面找到<input type=hidden name=userSession value=104241.774564601fDzVQDzpcHQVzzzHDcVHVpzVHzHf>其实就是每次红色部分的内容不同,所以我们要关联它。
    将代码前加上红色的函数,第一个参数是自己起的名字,LB是要关联部分的左边,RB是要关联部分的右边。将原来的value值用函数里面的第一个参数代替。

    Action()
    {
        web_reg_save_param("sessionID","LB=userSession value=","RB=>",LAST);
        web_url("WebTours",
            "URL=http://127.0.0.1:1080/WebTours/",
            "Resource=0",
            "RecContentType=text/html",
            "Referer=",
            "Snapshot=t1.inf",
            "Mode=HTML",
            LAST);
     
        lr_think_time(4);
     
         
     
        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=t2.inf",
            "Mode=HTML",
            ITEMDATA,
            "Name=userSession", "Value={sessionID}", ENDITEM,
            "Name=username", "Value=test1", ENDITEM,
            "Name=password", "Value=test1", ENDITEM,
            "Name=JSFormSubmit", "Value=on", ENDITEM,
            "Name=login.x", "Value=48", ENDITEM,
            "Name=login.y", "Value=10", ENDITEM,
            LAST);
     
        web_image("Search Flights Button",
            "Alt=Search Flights Button",
            "Snapshot=t3.inf",
            LAST);
     
        web_image("SignOff Button",
            "Alt=SignOff Button",
            "Ordinal=1",
            "Snapshot=t4.inf",
            LAST);
     
        return 0;
    }

    若有疑问或不正之处,欢迎提出指正和讨论。

  • 从软件测试用例看测试的问题及变化

    2010-12-01 16:50:10

    对于一个测试人员来说测试用例的设计与编写是一项必须掌握的能力。但有效的设计和熟练的编写却是一个十分复杂的技术,它需要你对整个软件不管从业务还是功能上都有一个明晰的把握。如何系统、结构的对用例加以规范将直接影响到其后的测试效率和效果,同时测试用例也将用来控制软件的整体执行覆盖,对最后的测试结果给出一种量化的评估标准。

      一、问题:

      许多测试类的书籍都有大幅篇章介绍用例的设计方法,如等价类划分,边界值,错误推断,因果图,判定表等。但实际应用中这些理论却不能给我们很明确的行为指导,尤其是业务复杂,关联模块紧密,输入标准和输出结果间路径众多时,完全的遵循这些方法只能让我们在心理上得到一种满足,而无法真正有效的提高测试效率,并且我们也没有足够的时间和资源编写完备的用例。通常我们只能依靠以前项目的用例编写经验(或习惯),希望能在这一个项目中更加规范,但多数情况下我们规范的只是“书写的规范”,在用例设计上以前存在的问题现在依旧。

      当好不容易用例基本完成,我们却发现面对随之而来的众多地区特性和新增需求,测试用例突然处于一种十分尴尬的境地:

      * 从此几乎很少被执行

      * 已经与程序的实现发生了冲突(界面变动,功能变动)

      * 执行用例发现的bug很少

      * 根本没有时间为新的功能需求增补用例

      * 有时间补充,但用例结构越来越乱

      * 特性的用例与通性用例之间联系不明确(以新增需求为主线列出所有涉及到的更改,但特性与通行之间的数据或业务联系在用例中逐渐淡化)

      知道怎样执行这个用例,但它要说明什么呢?(多数用例给我们的感觉是只见树木,不见森林,只说明某一功能的实现,无法串起)

      通过上面的一系列问题可以看到,似乎测试用例给我们带来的问题远多于益处,也正是因为在实际过程中遇到的问题积累,导致我们有很充分的理由忽视或拒绝用例的应用。

      但没有用例或简略用例的编写我们又会舒服很多么?不言自明,谁也不想倒退发展。

      二、原因:

      事实上我们在测试用例编写和设计上遇到的一系列问题只是一种表面的呈现,究其原因我认为有如下几点:

      1、没有适合的规范

      “适合的规范”或称“本地化的规范”。这是我们在测试过程中遇到的第一个问题,通常也是很容易习惯且淡忘的。我们拥有相当多的流程文档、指导步骤和书本上的定义,但它适合我们当前的项目么?

      每一个测试工程师在进入这个职业的初期都会了解一些测试上的概念和术语,进入公司或项目组后也会进一步学习相应的文档,例如怎样规范编写,怎样定义bug级别,软件实现的主要业务等。但当测试经理开始给我们分配某一模块的用例编写时,又有多少人知道该怎样去写,怎样写算是好?

    在测试论坛中常能看到介绍用例编写方法的帖子,而迷茫于怎样应用到实践的回复也不为少数。为何我们无法在公司和项目组内找到明确且适合的规范?于是我们只得选择从书本或之前的用例中复制,不管是结构还是方式都依赖于以往的经验,我并不是说这样就是错误的,但不能总结成文的经验无法给予测试更多帮助。我们有太多经验,但却没有形成适合的规范。

      2、功能与业务的分离

      我们知道怎样列举一个输入框的用例,但却很少考虑说明这个输入框是用来做什么的,如果仔细分析不难发现,用例中这种功能与业务的分离越来越普遍也越来越明显。

      边界值、等价类划分、因果图,这些用例方法是一种高度提纯的方法,本身就很偏向于功能及代码,所以怎样编写业务的用例我们就从理论上失去了参考。

      复杂的业务会贯穿于整个软件,涉及众多功能点,里面组合的分支更不可胜数。测试用例务求简洁、明确,这一点也与业务“格格不入”。功能用例依赖程序界面,业务描述依赖需求文档。于是我们更偏向于根据已实现的界面编写功能用例,列举出众多的边界值、等价类。流程的操作只有凭借经验和理解,这时测试出的bug是最多的,但我们却无法使这个bug对应到一个用例中(点击一个按钮报出的错误有时原因并不在这个按钮或按钮所在的窗体),只能自己添加note向开发人员指出可能出错的源头。正因为我们没有很好的积累业务上的用例,才使得我们感到执行用例时发现的bug不多。

      用例结构的划分一定程度上也造成了功能和业务的分离,依照界面模块建立文件夹,并在其中新建不同用例,这使得用例从结构上就很难联通起来。

      3、测试未能跟上变化

      变化!想象一下,当我们越来越多的听到开发人员在那里高呼“拥抱变化”“敏捷开发”的时候,测试又有什么举措呢?当地区特性,软件版本越来越多的时候,测试是否在积极响应呢?变化是我们面临的最大挑战,我认为测试未能跟上变化是造成测试过程中遇到种种问题和矛盾的主要原因。

      对需求和程序的变化测试人员的感受是非常深的,测试总是跟在需求和开发后面跑,使得所有风险都压在自己身上。不断压缩的时间和资源使我们只能放弃那些“不必要”的工作:尽快投入测试,尽快发现bug,而非从整体把握软件的质量情况,统筹策略。

      疲于应对的直接影响就是程序质量无法准确度量,进度无法控制,风险无法预估。用例与程序脱节,新增用例混乱和缺少。长此以往我们只得放弃修改、增补用例,甚至放弃之前积累的所有成果。用例变为程序变更的记录摘要,没有测试数据的保留,测试步骤和重点无法体现,新加功能与原来的程序逐渐“脱离”,可能还会出现相互违背的情况,但这我们却无法及时发现。

      永远是变化决定我们的下一步工作,这也是混乱的开始。

      三、可能的解决办法:

      上面的问题也许在成熟的公司和项目组内很少遇到,而遇到问题的也需根据不同的情况单独考虑。分析错误并不能给我们带来成功,而成功的特质也不会尽为相同。所以在这里我希望以探讨的方式提出一些可能的解决办法,不拘泥形式,以结果来确定,最适合的就是最好的。

      1、测试驱动开发,用例指导结果,数据记录变化

      “测试驱动开发”(TDD) 是一个比较新的概念,在网上可以看到很多介绍文章,它主要讨论如何让开发的代码更奏效(Work)更洁净(Clean),“测试驱动开发的基本思想就是在开发功能代码之前,先编写测试代码”。可以看到,TDD是建立在“代码”级别的驱动,但目前我们需要探讨的问题是怎样在黑盒测试中做到“测试驱动开发”。

    首先我们需要纠正一个态度,很多人认为黑盒测试的技术含量不高,可思考可拓展的内容不多,主要的工作就是用鼠标在那里瞎点,于是很多“高级”的技术方法都试图与黑盒测试划清界限。但测试人员发现的bug有80%以上都是黑盒测试发现的,手工操作软件仍是目前检验软件质量最有效的一种方法。

      如何在黑盒测试中做到测试驱动开发?我认为可以从用例级别做起,以业务用例指导实现的结果。

      开发人员通常比较关注技术,对于业务上的理解容易忽视并出现偏差,而需求文档又不会很全面的指出应该实现怎样的结果,这就使得从业务到功能出现一个“阅读上的障碍”,如果最后发现程序错了还需返工,这样耗费的人力物力就非常大了。测试人员和最终用户不用过分关心软件实现的细节,所以以业务用例驱动开发,就是一个比较好的方法。给出一个明确的预期结果,指导开发人员如何界定是否达成目标,同样这也需要运用测试中的各种方法,列举出业务流程里数据的等价类和边界值。

      业务用例的构造要先于程序实现,与需求和开发人员沟通一致,并以此作为一个基准,保证程序实现不会出错,还能对整个软件的进度和质量有一个很好的估计和度量。业务用例可以不关注程序的界面,但一定要有数据的支持。这就是测试主导变化的另一点“数据记录变化”。

      我们不仅要应对变化,还要记录变化,使测试用例成为对程序持续性的监控,数据可以作为最基本、最简单的支持。当一个业务很复杂时可以拆分成段 (业务段与程序中以窗体或页面的划分是不一样的),使用典型的用例方法列出实际输入和预期结果。我们希望数据能做到通用和共享,最理想的情况就是建立一个 “数据库”,每个业务用例都从“数据库”中取得输入数据和预期结果,这个数据只是针对业务入口和出口的,当程序内部设计变更时,保留的数据不会因此而作废。举一个例子,例如我的程序要从某种文件中读取数据并计算结果,一段时间后程序内部字段增加了,如果是以保存的文件附件方式提供数据,则现在程序很可能就打不开这个文件了。使用“数据库”指导测试人员可以在变化的程序里直接针对业务输入,而不关心程序内部结构。

      再进一步的话“数据库”就开始涉及到程序内部的接口,属于单元和集成测试,这需要开发人员的配合。

      2、为用例标明时间(版本)和优先级

      为测试用例标明时间或版本可以起到一种基准的作用,标明项目进度过程中的每一个阶段,使用例直接和需求基线、软件版本对应。同样这需要规范流程,也是对变更的一种确认和控制。或者可以为用例增加一个状态,指明这个用例目前是否与程序冲突,当程序变更时改变用例的状态,并更新用例版本。

      为测试用例标明优先级可以指出软件的测试重点、用例编写的重点,减少用例回归的时间,增加重点用例执行的次数,帮助项目组新人尽快了解需求,在自动化测试的初期也可以参考这个优先级录制脚本。

      3、功能用例与业务用例分开组织

      为业务用例单独开辟出一种分类,将功能用例与业务用例分开组织,按照不同关注点列举执行路径。业务用例应在开发前或同期编写,帮助测试人员和开发人员明确业务,了解正确流程和错误流程。功能用例更依赖于程序界面的描述,但功能用例并不等于使用说明。对某些模块的等价类、边界值测试会发现很多严重的bug,也许与业务无关,但用户往往很容易这样操作(例如登录名,你是否考虑到很长的名字,或者用户的键盘有问题,总是敲入n多空格在里面,这与业务无关,但程序将会怎样处理?)。

      4、审核用例,结对编写

      测试组长或经理对用例进行审核可以做到用例的补充和校对,但一般情况下是很难做到的,我们可以采用另一种方法,就是结对编写测试用例(前提是你有两个以上的测试人员),内部审核。

      测试用例不是自己编写自己执行,它需要其他测试人员都能读懂且明白目标所指。结对编写可以尽量减少个人的“偏好习惯”,同时也能拓展思维,加强测试重点的确认,小组内部达到统一。一定程度上结对编写也可以减少组长或经理对用例的管理负担,提高组员的参与积极性。

      四、发展

      上面的这些解决方法只是一种建议,具体如何实施到项目中还需根据情况而定。同时即使我们正在积极的寻求改变,我们还是会碰到无数的新问题和新苦恼,也许会比以前更为众多,这是我们必须付出的。

      可以看到测试的发展方向很多很广,即使传统的黑盒测试并不是毫无新意,高级的测人员必须同时在测试技巧和专业领域方面都有很高的“修为”。测试工作怎样更适合我们而发展,将给予我们更多的思考。

  • Loadrunner Port Mapping录制方法

    2010-12-01 10:45:31

    Loadrunner Port Mapping录制方法(一种通用的录制脚本的方法)


    以下观点仅是本人对loadrunner的理解所写,不一定正确,仅供大家参考!如果有不对的地方还请指出!

    我们在很多时候因为无法录制脚本而困惑,如loadrunner使用FTP协议录制Core FTP Lite时,只能看到loadrunenr捕获到事件,但无法生成脚本,又比如使用POP3或SMTP协议录制邮件收发时也是只能捕获到时间,但无法生成脚本,此时我介绍一种万能的录制方法可以解决这个问题。

    其实loadrunner录制的基本原理就是通过Port Mapping这个方法实现的,只不过我们在平时没有在意而已,那么先来介绍一下loadrunner典型的录制原理,再介绍一下loadrunner使用Port Mapping方式的录制原理。

    通常情况下,我们在录制脚本的过程是:

    1、  选择协议

    2、  输入要录制的URL或指定要录制的应用程序的路径

    3、  选择录制脚本的位置,存放在哪个ACTION中

    4、  设置录制选项

    5、  开始录制

    此时loadrunner会自动监控你所指定的URL或应用程序所发出的请求及服务器返回的响应,它做为一个第三者监视着客户端与服务器端的所有对话,然后把这些对话记录下来,生成脚本,再次运行时模拟客户端发出的请求,捕获服务器端的响应。它在做监视时会自动捕捉客户端发出请示时所用的端口,并根据请求内容向服务器端的相应端口发送,而服务器回应时,根据请求消息中的端口向客户端某个端口发出回应,也就是说loadrunner监听的端口是由应用程序或请求所决定的。

       好了,知道通常情况下loadrunner录制脚本的原理后,我们在说一下Port Mapping录制脚本的原理:

       Port Mapping录制过程是:

       我们已使用POP3协议录制收邮件的过程来举例

    1、  选择协议,选择POP3协议

    2、  在要录制的应用程序路径中输入“loadrunner安装路径\bin\wplus_init_wsock.exe”

    3、  设置录制选项,在NETWORK的Port Mapping中新建一个代理

    根据下图新建一个代理:

    在socket service设置中,要指明你客户端程序要连接的服务器地址、端口、协议、录制脚本的类型等选项,由于负责收邮件的服务器默认的端口是110,所以我们这里将PORT设置为110;

    在TRAFFIC FORWARDING设置中,这个端口可以为任意端口,主要是为了让loadrunner启动的代理服务知道要把所有发往指定端口111的请求转发给指定服务器的110端口

    原理图如下:

    4、  设置要录制的程序端口

    将要录制的应用程序原本发往外网服务器的请求发送到本地的loadrunner启动的代理程序上,以便loadrunner进行监控

    设置发送到本地的111端口:

    5、  完成以上设置后,就可以开始录制脚本了

    在录制脚本时我们会看到loadrunner会启动一个代理程序

     

     这个就代表我们设置的代理服务正常启动了。通过重新录制我们不仅看到了loadrunner捕获到了事件,而且正确生成了脚本。

    好了,总结一下Port Mapping录制的录制原理:

    首先将要录制的应用程序向服务器发送请求的服务器地址和端口改为本地计算机及111端口,在Port Mapping中设置的代理程序再将发往111端口的所有请求转发给真正的服务器端,相当于loadrunner强制性的将本地某端口的通讯录制了下来,通过这个代理更明确了需要监控的端口和协议。

    其实loadrunner通常使用的录制方式也是采用这样一个原理,只不过我们并不知道它具体监控的是哪个端口,而在Port Mapping录制方法中我们明确的指定了监听端口。可以说通常的录制方法是一种隐性的,而后一种录制方法是显性的。

    可以通过此方法录制我们现有的所有协议的脚本,不会再出现只看到事件无法生成脚本的情况了!

    其实这才是一种通用的录制方法!

  • LR 协议选择

    2010-12-01 09:54:03

       很多时候一提到不是基于浏览器的应用,很多人就会想到用WinSocket协议来录制,仿佛Form窗体都可以用Winsocket 。

       从道理上讲网络通讯的底层都是基于Socket的,例如TCP、UPD等,似乎所有的程序都可以用Socket协议来录制。但是事实不是这样的,因为选择的协议决定了LoadRunner如何捕获数据包。否则会多捕获很多无用的数据。

       因此,不是所有的程序都是适合WinSocket协议的。实际上,那些基于Socket开发的应用才真正适合Socket协议来进行录制。其他的,例如基于数据库的应用,就不太时候Socket协议,甚至可能录制不到脚本。

       很多C/S程序,一定要选择合适的协议。根据作者的经验,C/S的程序多数需要手工开发很多脚本,因为录制的很多回放时候或多或少都会有些问题,但是可以参考录制的结果。

       所以测试一个程序,一定要搞清楚开发人员用了什么技术、数据流是什么协议封装的。

     

  • 更改虚拟机硬盘大小

    2010-11-01 10:36:03

    由于第一次使用虚拟机,第一次操作,结果给操作系统分硬盘的时候只分了8G,根本不够用,于是想办法进行硬盘扩容,操作如下:
    1.在扩展硬盘之前,请确保物理硬盘有足够的空间以供扩展。
    2.关闭所有虚拟机,推出所以虚拟机程序。
    3.在DOS下(开始-->运行,输入cmd出现命令行工具)将目录定位到你的虚拟机的安装目录,如:C:\Program Files\VMware\VMware Workstation。
    4.输入vmware-vdiskmanager回车,运行vmware-vdiskmanager.exe。出现相应的命令帮助。
    以下为vmware扩展硬盘分区的方法。

    vmware安装目录下,vmware-vdiskmanager.exe在dos提示符下运行,不加任何参数时,给出此程序使用方法:
    ---------------------------------------------------------------------
    VMware Virtual Disk Manager - build 12544.
    Usage: vmware-vdiskmanager.exe OPTIONS diskName | drive-letter:
    Offline disk manipulation utility
       Options:
         -c                   : create disk; need to specify other create options
         -d                   : defragment the specified virtual disk
         -k                   : shrink the specified virtual disk
         -n      : rename the specified virtual disk; need to
                                 specify destination disk-name
         -p                   : prepare the mounted virtual disk specified by
                                 the drive-letter for shrinking
         -q                   : do not log messages
         -r      : convert the specified disk; need to specify
                                 destination disk-type
         -x      : expand the disk to the specified capacity
         Additional options for create and convert:
             -a        : adapter type (ide, buslogic or lsilogic)
             -s          : capacity of the virtual disk
             -t      : disk type id
         Disk types:
             0                 : single growable virtual disk
             1                 : growable virtual disk split in 2Gb files
             2                 : preallocated virtual disk
             3                 : preallocated virtual disk split in 2Gb files
         The capacity can be specified in sectors, Kb, Mb or Gb.
         The acceptable ranges:
                               ide adapter : [100.0Mb, 950.0Gb]
                               scsi adapter: [100.0Mb, 950.0Gb]
             ex 1: vmware-vdiskmanager.exe -c -s 850Mb -a ide -t 0 myIdeDisk.vmdk
             ex 2: vmware-vdiskmanager.exe -d myDisk.vmdk
             ex 3: vmware-vdiskmanager.exe -r sourceDisk.vmdk -t 0 destinationDisk.vm
    dk
             ex 4: vmware-vdiskmanager.exe -x 36Gb myDisk.vmdk
             ex 5: vmware-vdiskmanager.exe -n sourceName.vmdk destinationName.vmdk
             ex 6: vmware-vdiskmanager.exe -k myDisk.vmdk
             ex 7: vmware-vdiskmanager.exe -p m:
                   (A virtual disk first needs to be mounted at m:
                   using the VMware Diskmount Utility.)
    -------------------------------------------------------------
    修改虚拟硬盘尺寸的参数,有两个.
    vmware-vdiskmanager -x 60GB yourVdisk.vmdk

    注意事项:
    1.yourVdisk.vmdk 应该是一个绝对路径,因为路径中有空格出现,所以应该在路径上加双引号即可。
    如:vmware-vdiskmanager -x 30GB "D:\MyVirtualPC\Windows Server 2003 Enterprise Edition\Windows Server 2003 Enterprise Edition.vmdk";括号必须扩在整个路径上,一个双引号即可。输入完回车,等待。

    2.扩展成功后打开虚拟机,进入相应的操作系统,在“我的电脑”->管理->磁盘管理->就可以看到已经增加的空间,格式化后可显示出来。

     

  • LR 227个问题

    2010-10-27 11:59:01

  • Web 系统性能测试_文档模板

    2010-10-27 11:56:17

  • 内存和cpu

    2010-10-27 11:53:33

      内存的频率越高,传输速率也就越高,提供的带宽也就越大!自然,CPU的前端总线频率越高,要求的带宽也就越大。如果内存提供的带宽达不到要求的话,就会影响CPU性能的发挥,举个例子:按理说CPU这里,一天可以处理100辆车的数据,需要4车道的路才能让这100辆车通过,内存也就需要提供4车道的带宽,但这条内存频率低,只能提供2车道的带宽,这样自然无法通过100辆车了,CPU处理的数据也就少了,也就慢了!明白了吧?
      现在的主板都支持双通道内存了,插两条一样的内存,可以提供更大的带宽,所以很多装机高手,都选择两条内存!
      具体前端总线频率多高,要求多大的带宽,就不太清楚了,你可以自己查一下!
      总结就是:CPU的总线频率越高,要求的内存频率也就越高!所以,现在推出了DDR3内存,频率最高可达1600MHz,提供的带宽更高!

      CPU和内存搭配这涉及到3方的关系,分别为:
    1.北桥支持的FSB频率和内存类型(不管怎么搭配,如果主板先就是鸡肋,怎么搭配不到哪去。。。)
    2.CPU的FSB
    3.内存的工作频率
      北桥支持的FSB和内存类型很简单,用EVEREST一看就知道,比如PM45支持CPU FSB1066和内存DDR2 1066(甚至1333,只举例DDR2),需要注意的就是 内存频率:CPU FSB大于等于1:1时是比较好的,一般1:1就比较合理了,大于1:1属于内存异步范畴,因为升级U的成本远远高于升级内存,所以内存频率高于U比较经济。那么这里是不是FSB1066就一定要搭配1066的内存呢,实际不是,比如两条DDR2 533组成双通道就构成1066了,这样就和FSB1066同步了,按照现在的准系统搭配,2条DDR2 800的条子配FSB 1066的U已经很不错了,如当然如果组成DDR2 1066双通道会更好点,这取决于你的成本预算,也就是并不是说FSB和内存频率一定要同步。简单的说内存频率最好是接近cpu的前端总线,这样才不至于造成瓶颈,内存频率和CPU外频要同步或大于CPU,(但是要考虑内存频率和CPU外频比值大于1:1时内存的延时情况,延时过长可能反而导致性能低于1:1,当然按照现在的技术延时是越来越低)。另外一种情况是考虑CPU的工作频率和内存工作频率同步,CPU FSB的工作频率一般就是除以4,比如FSB1066实际工作频率为1066/4=266,DDR2内存也是,DDR2 800工作频率为400,这种情况下那么要保证CPU和内存工作频率同步就要超频了,这个涉及的就比较晕了,可以软超也可以BIOS硬超,实际一般情况下为了搭配内存频率和CPU频率合理只要频率(不是工作频率)大于等于1:1就可以了,超频致使工作频率一样会使性能更好点而已。
      cpu和内存哪个超出一定的范围都是浪费的,他们之间是有一定的联系的。
      由于数据传输最大带宽取决于所有同时传输的数据的宽度和传输频率,即数据带宽=(总线频率×数据位宽)÷8。目前PC机上所能达到的前端总线频率有266MHz、333MHz、400MHz、533MHz、667MHz、800MHz、1066MHz、1333MHz几种,前端总线频率越大,代表着CPU与内存之间的数据传输量越大,更能充分发挥出CPU的功能。
      带宽=总线宽度×总线频率×一个时钟周期内交换的数据包个数。很明显,在这些乘数因子中,每个都会对最终的内存带宽产生极大的影响。然而,如今在频率上已经没有太大文章可作,毕竟这受到制作工艺的限制,不可能在短时间内成倍提高。而总线宽度和数据包个数就大不相同了,简单的改变会令内存带宽突飞猛进。
      显存位宽的计算方法是:单块显存颗粒位宽×显存颗粒总数,而显存频率则是由"1000/显存颗粒纳秒数"来决定。一般来说,我们可以从显存颗粒上一串编号的最后2两位看出其纳秒数,从中也就得知其显存频率。
      内存带宽计算公式: 
    内存带宽=内存工作频率X内存总线宽度/8(计算带宽时位和字节的换算)
    SDR和DDR都是64bit的总线宽度,只是DDR可以在时钟信号的上升沿和下降沿都传送数据,因此计算得到的相应带宽要X2。RDR的总线带宽较窄,只有16bit,但RDR在工作时使用的是对称传输的工作模式,因此计算得到的相应带宽也要X2。
      DDR266、333的命名方式是因为DDR的理论效能是SDR的两倍,为了区别SDR,所以分别以SDR工作频率的两倍来区分DDR,分别映射在133、166MHz工作下的DDR。PC1600、2100、2700的命名方式是指DDR的实际带宽,分别映射在100、133、166MHz工作下的DDR。
      产生CPU和Memory瓶颈的原因:
    现今的存储器速度远远低于处理器的速度。外频并不一定是CPU的BIU(Bus Interface Unit)的工作频率。现在的总线的速度一般在200MHz到500MHz。和CPU一样,总线的工作频率也是通过倍频得到的。现在的主板提供的内存异步工作模式都是内存频率小于总线频率。比如Bus工作在266MHz下,内存工作在100MHz下。
    你说的266应该是DDR内存吧,266MHz的DDR,clock还是133MHz。如果你的Duron是100MHz的外频,也就是说Bus的频率是200MHz,那么你的DDR的工作clock也是100MHz。

     

  • cpu

    2010-10-27 11:52:50

    us 用户空间占用CPU百分比
    sy 内核空间占用CPU百分比
    ni 用户进程空间内改变过优先级的进程占用CPU百分比
    id 空闲CPU百分比
    wa 等待输入输出的CPU时间百分比
  • 网卡

    2010-10-27 11:52:25

    网卡是工作在数据链路层的网路组件,是局域网中连接计算机和传输介质的接口,网卡是工作在数据链路层的网路组件,是局域网中连接计算机和传输介质的接口,不仅能实现与局域网传输介质之间的物理连接和电信号匹配,还涉及帧的发送与接收、帧的封装与拆封、介质访问控制、数据的编码与解码以及数据缓存的功能等。
    网卡的一个重要功能就是要进行串行/并行转换。
    连接计算机和局域网;
    集成的占用CPU,和内存的容量~~而独立的不占用;
    网卡也叫“网络适配器”,英文全称为“Network Interface Card”,简称“NIC”,网卡是局域网中最基本的部件之一,它是连接计算机与网络的硬件设备。无论是双绞线连接、同轴电缆连接还是光纤连接,都必须借助于网卡才能实现数据的通信。
    网卡的主要工作原理是整理计算机上发往网线上的数据,并将数据分解为适当大小的数据包之后向网络上发送出去。对于网卡而言,每块网卡都有一个唯一的网络节点地址,它是网卡生产厂家在生产时烧入ROM(只读存储芯片)中的,我们把它叫做MAC地址(物理地址),且保证绝对不会重复。

  • 光驱、软驱、光雕区别

    2010-10-27 11:51:54

    光驱
    光驱是用来解读光盘的,分为VCD光驱和DVD光驱,现在目前VCD光驱基本上已经被淘汰了,都是用DVD光驱。直接帮光碟放进去就可以使用。

    软驱
    软驱是用来解读软盘的,现在由于技术的更新,软盘的容量小,体积大,逐渐被U盘所取代。所以现在软驱基本上也接近淘汰,现在新装机的电脑都不安装软驱了。

    光雕
    光雕就是用来刻盘的,用户可以根据自己的意见把自己需要的文件刻到一张空盘里。而且光雕最大的一个特点是还可以在光蝶的版面上画图画和书写自己喜欢的文字,比如你可以在上面写个“某某装机专用碟”什么的。但是写字和画图画需要买特殊的光雕碟(普通空白碟只要3块钱左右,4,5GB,光雕碟要10多块钱)


    光盘存储介质大,容量大且数据安全性高,不易丢失数据。而软盘容量小,且容易损坏,容易丢失数据,现在用U盘很不错,既安全又容量大!

    很简单的理解为软驱是方形的并且现在很少使用由于它的储量小几乎连几首歌都储不了.所以现在几乎淘汰了.不要再去了解它了.

  • dos命令

    2010-10-27 11:50:44

    convert c:/FS:NTFS   转化文件格式
    nslookup -type=mx 域名

381/212>
Open Toolbar