发布新日志

  • 揭秘QTP之Reporter对象

    2012-07-13 15:10:07

    揭秘QTP之Reporter对象

    2008-10-23 作者:陈能技 来源:IT168

     

    本文介绍了Reporter对象的几个鲜为人知的方法,利用LogEvent、SetContext、UnSetContext方法,可以实现日志的结构化、层次化写入,让你的QTP测试报告看起来更加有条理、分类清晰。

    QTP的日志其实有很多的缺点,尤其是当你的脚本依赖函数来执行的时候,因为函数执行时调用Reporter对象来写日志,只会顺序从上到下、扁平、不分类地写下去,如图1所示。

    图1 函数执行时调用Reporter对象写日志的问题

    而不像在Action中写日志一样,按一定的层次关系来写日志(例如根据调用的关系嵌套)。那么如何让函数调用Reporter对象来写日志时也具备一定的层次关系,让日志展现更加灵活、分类清晰呢?其实QTP的Report对象中暗藏了不少可利用的属性和方法。

    Report对象的“私家珍藏”

    在关键字视图通过InterlliSense查看QTP的Report对象时(如图2所示),可发现仅有3个属性、1个方法可用,其中最常用的是ReportEvent方法。

    图2 Reporter对象的公开方法和属性

    而实际上,QTP的Report对象还有其他的方法可用,这些方法并没有对外公开,例如可用LogEvent方法来在日志树中写入一个节点:

    ' 使用Reporter对象的LogEvent写入新节点
    intContext = Reporter.LogEvent("User", dicMetaDescription, Reporter.GetContext)

    用SetContext方法把某个节点作为父节点,这样的话,后续写入的Log都将在这个父节点之下:

    ' 调用Reporter对象的SetContext把新写入的节点作为父节点
    Reporter.SetContext intContext

    如果要退出该节点,返回日志树的上一层,则可调用UnSetContext方法即可,如下脚本所示:

    '调用Reporter对象的UnSetContext,返回上一层
    Reporter.UnSetContext

    Report对象“解密”

    有了LogEvent、SetContext、UnSetContext这几个方法,我们就可以实现日志的结构化、层次化写入了,例如下面的例子所示:

    ' 用一个Dictionary对象来存储节点的信息
    Set dicMetaDescription = CreateObject("Scripting.Dictionary")

    ' 设置节点的状态
    dicMetaDescription("Status") = MicDone

    ' 设置节点的名称
    dicMetaDescription("PlainTextNodeName") = "父节点"

    ' 设置节点的详细描述信息(可以使用HTML格式)
    dicMetaDescription("StepHtmlInfo") = "<DIV align=left><H1>这是一个拥有孩子的节点</H1><b>Hello!</b> How are you!.</DIV>"

    ' 设置节点的图标
    dicMetaDescription("DllIconIndex") = 210
    dicMetaDescription("DllIconSelIndex") = 210

    ' 节点图标从ContextManager.dll这个DLL文件中读取
    dicMetaDescription("DllPAth") = "D:\Program Files\HP\QuickTest Professional\bin\ContextManager.dll"

    ' 使用Reporter对象的LogEvent写入新节点
    intContext = Reporter.LogEvent("User", dicMetaDescription, Reporter.GetContext)

    ' 调用Reporter对象的SetContext把新写入的节点作为父节点
    Reporter.SetContext intContext

    ' 后续写入的Log都将在这个父节点之下
    Reporter.ReportEvent MicPass, "Step1", "Step1 Pass!"
    Reporter.ReportEvent MicWarnning, "Step2", "Step2 Pass With Warnning!"
    Reporter.ReportEvent MicFail, "Step2", "Step2 Fail!"
    '调用Reporter对象的UnSetContext,返回上一层
    Reporter.UnSetContext

    ' 在父节点之外写Log
    Reporter.ReportEvent MicPass, "Case2", "Case2 Pass!"

    在这个例子中,我们首先用一个Dictionary对象来存储节点的信息,其中Status表示节点的状态,例如MicDone就表示完成状态;PlainTextNodeName表示节点的名称;StepHtmlInfo表示节点的详细内容,可以用HTML格式来写入;还可以用DllIconIndex、DllIconSelIndex、DllPAth这3个属性来表示节点的图标。

    然后使用Reporter对象的LogEvent把Dictionary中存储的信息写入一个新节点,再调用Reporter对象的SetContext把新写入的节点作为父节点,这样后续写入的Log都将在这个父节点之下,最后调用Reporter对象的UnSetContext,返回上一层,这样后续写入的Log将在这个父节点之外。

    这个例子的运行结果如图3所示。

    图3 脚本运行结果

    封装成可重用的函数

    可以把上面的代码封装成一个可重用的函数,以方便调用。方法如下:

    (1)首先在QTP中使用“Function Definition Generator”来定义一个名为“EnterNode”的函数。如图4所示。

    图4 定义函数EnterNode

    该函数实现的是把日志写入某个节点下,输入参数为NodeName(节点的名称),NodeContent(节点的描述信息)。

    函数的脚本如下:

    '@Description 指定把日志写入节点下
    Public Function EnterNode(ByRef NodeName, ByRef NodeContent)

    ' 用一个Dictionary对象来存储节点的信息
    Set dicMetaDescription = CreateObject("Scripting.Dictionary")

    ' 设置节点的状态
    dicMetaDescription("Status") = MicDone

    ' 设置节点的名称
    dicMetaDescription("PlainTextNodeName") = NodeName

    ' 设置节点的详细描述信息(可以使用HTML格式)
    dicMetaDescription("StepHtmlInfo") = NodeContent

    ' 设置节点的图标
    dicMetaDescription("DllIconIndex") = 210
    dicMetaDescription("DllIconSelIndex") = 210

    ' 节点图标从ContextManager.dll这个DLL文件中读取
    dicMetaDescription("DllPAth") = "D:\Program Files\HP\QuickTest Professional\bin\ContextManager.dll"

    ' 使用Reporter对象的LogEvent写入新节点
    intContext = Reporter.LogEvent("User", dicMetaDescription, Reporter.GetContext)

    ' 调用Reporter对象的SetContext把新写入的节点作为父节点
    Reporter.SetContext intContext
    End Function

    (2)然后再定义一个与“EnterNode”相应的函数“ExitNode”,如图5所示:

    图5 定义函数ExitNode

    ExitNode函数用于退出当前日志节点,需要与EnterNode配对使用,函数的脚本如下所示:

    '@Description 退出当前日志节点(与EnterNode配对使用)
    Public Function ExitNode
    '调用Reporter对象的UnSetContext,返回上一层
    Reporter.UnSetContext
    End Function

    (3)有了EnterNode和ExitNode函数后,我们就可以像下面的例子一样使用EnterNode和ExitNode,实现日志记录的结构化、层次化写入:

    ' 进入节点
    EnterNode "父节点","<DIV align=left><H1>这是一个拥有孩子的节点</H1><b>Hello!</b> How are you!.</DIV>"

    ' 在节点内写Log
    Reporter.ReportEvent MicPass, "Step1", "Step1 Pass!"
    Reporter.ReportEvent MicWarnning, "Step2", "Step2 Pass With Warnning!"
    Reporter.ReportEvent MicFail, "Step2", "Step2 Fail!"

    ' 退出节点
    ExitNode

    ' 在节点之外写Log
    Reporter.ReportEvent MicPass, "Case2", "Case2 Pass!"

    该脚本首先调用EnterNode,创建一个"父节点",并自动进入该节点的层次下,然后使用普通的ReportEvent方法写日志,当需要退出该"父节点"时,就调用一下ExitNode,则后续的日志都在该节点之外写。

    小结

    本文介绍了Reporter对象的几个鲜为人知的方法,利用LogEvent、SetContext、UnSetContext这几个方法,可以实现日志的结构化、层次化写入,让你的QTP测试报告看起来更加有条理、分类清晰。

    不知道为什么QTP的帮助文档中没有列出这几个有用的方法,但是不管怎样,在我们揭开了Reporter对象的这些隐藏的方法后,我们就可以充分利用它们为我们服务,让我们的自动化测试脚本更加强大。

  • QTP中Wait与同步点的区别

    2012-07-10 16:27:50

    先说wait函数,当脚本走到wait函数时,就开始执行这个函数.如:wait(10),就等待10秒种后再继续执行下面的语句.wait函数的这个等待的时间,那相对来说是比较固定的.如上例子,一定要等待完10秒后再执行.所以写脚本的时候要自己估算一下时间.不然可能造成时间的浪费,或者等待时间的不足.

        那同步点呢.等待时间就比较灵活了,它的等待时间是不固定的.设置同步点后,当脚本执行到这句话后,脚本就开始执行等待.脚本会在规定时间内不断的去检查,所同步的对象有没有出现,一但出现,脚本就继续往下执行.不需要等完所有规定时间.如果在规定的时间内,所要同步对象还没有出现,那就提示超时的错误信息.

    Window("Flight Reservation").ActiveX("Threed Panel Control").WaitProperty "text", "Insert Done...", 10000


        当脚本执行到这句话时,就开始执行同步等待时间.这里设置超时时间为10000毫秒(10秒).在这个时间内,脚本会不断去查看该对象的text属性的属性值Insert Done...,有没有出现.一但同步到这个属性值,就开始执行下面的脚本了.而不需要再继续等待,直到1000秒结束为止.那这样的话,这个等待时间不用自己去控制,设置好后由程序自己去判断,就比较灵活,也不会出现浪费时间的情况.能提高脚本的执行率.

    举例:

    Sub WaitProperty_Example()
    'The following example uses the WaitProperty method to make the
    'test wait until the ActiveX "Calendar" object is enabled, or for
    '5 seconds (5000 milliseconds) to pass. If the object is enabled before
    '4 seconds pass, QuickTest clicks the object.
    With Window("Date").ActiveX("Calendar")
        If .WaitProperty("Enabled", True, 5000) = False Then
            Reporter.ReportEvent 1, "Calendar", "Object Disabled"
        Else
            .Click 95, 100
        End If
    End With
    End Sub
  • GetROProperty,GetTOProperties,GetTOProperty区别

    2012-07-10 16:25:19

    原文地址:http://bbs.51testing.com/thread-87074-1-2.html  作者:yabest

    呵呵,这个文章本来是在给别人的帖《GetROProperty,GetTOProperties,GetTOProperty的区别》回复时写的,没有独立出来。
    http://bbs.51testing.com/viewthread.php?tid=13554&page=1#pid369327
    写完后,在网上被多处转载。没想到今天又被转贴回51testing来了。

    虽然在精华版里,也赚了几朵花,但没独立总是不方便。所以修整了一下,独立成帖,以方便大家阅读。
    欢迎大家拍砖!(当然更欢迎大家送花sdlkfj5)


    一、QTP识别对象的原理(by yabest)

    QTP里的对象有两个概念,一个是Test Object(简称TO),一个是Runtime Object(简称RO)。
    这两个概念从字面上不大好理解,也容易混淆。
    但从实际作用上来看,应该说TO就是是仓库文件里定义的仓库对象,RO是被测试软件的实际对象。

    QTP识别对象,一般是要求先在对象仓库文件里定义仓库对象,里面存有实际对象的特征属性的值。
    然后在运行的时候,QTP会根据脚本里的对象名字,在对象仓库里找到对应的仓库对象,
    接着根据仓库对象的特征属性描述,在被测试软件里搜索找到相匹配的实际对象,最后就可以对实际对象进行操作了。

    仓库对象TO一般在录制/编写脚本时加入仓库文件,它不仅可以在录制编写时进行修改,
    也可以在运行过程中进行动态修改,以匹配实际对象。

    和TO、RO相关的几个函数有:

    GetTOProperty():取得仓库对象的某个属性的值
    GetTOProperties():取得仓库对象的所有属性的值
    SetTOProperty():设置仓库对象的某个属性的值

    GetROProperty():取得实际对象的某个属性的值

    (注:这几个函数访问的都是对象的封装属性,不是对象的自身属性,封装属性和自身属性的区别详见后面第二章QTP操作对象的原理)

    理解了TO的含义,你就可以自由的用SetTOProperty()定义TO,以灵活的操作RO

    比如有个测试任务,窗口上有很多待检查的记录,每条记录右边都有一个Check按钮,用来检查各条记录。
    记录个数不定,所以Check按钮个数也就不定,只有一个Edit显示记录个数。
    我们要对每条记录进行检查,也就是要点击每个Check按钮。
    但是Check按钮个数不定,不好录制,而且个数可能也很多(上百个),即使能一一录制,那也很麻烦。

    那我有一个好办法,只录制一个按钮对象,它设有两个特征属性 label=OK, index=0
    然后用下面的脚本,就可以完成测试

    buttonNum = CInt(JavaWindow("Test").JavaEdit("Record Num").GetROProperty("value"))
    For buttonIndex = 0 to buttonNum - 1
      JavaWindow("Test").JavaButton("Check").SetTOProperty("index", buttonIndex)
      JavaWindow("Test").JavaButton("Check").Click
    Next


    或者窗口上有New、Modify、Delete、Check等好几个按钮,要把这几个按钮一一按过去
    我在对象仓库里只设置一个按钮对象AnyButton,label特征属性值填任意值,然后用下面脚本执行测试

    JavaWindow("Test").JavaButton("AnyButton").SetTOProperty("label", "New")
    JavaWindow("Test").JavaButton("AnyButton").Click

    JavaWindow("Test").JavaButton("AnyButton").SetTOProperty("label", "Modify")
    JavaWindow("Test").JavaButton("AnyButton").Click

    JavaWindow("Test").JavaButton("AnyButton").SetTOProperty("label", "Delete")
    JavaWindow("Test").JavaButton("AnyButton").Click

    JavaWindow("Test").JavaButton("AnyButton").SetTOProperty("label", "Check")
    JavaWindow("Test").JavaButton("AnyButton").Click

    另外,QTP还支持脚本描述的方法来定义和访问对象,即不需要在仓库里定义,也能访问和操作实际对象
    ( Written by yabest )

    如上面两个任务,可以如下实现

    1. 不需要在仓库里定义Check按钮对象,直接用下面脚本来实现测试

    buttonNum = CInt(JavaWindow("Test").JavaEdit("Record Num").GetROProperty("value"))
    For buttonIndex = 0 to buttonNum - 1
      JavaWindow("Test").JavaButton("label:=Check", "index:="+CStr(buttonIndex)).Click
    Next

    2. 不需要在仓库里定义New、Modify、Delete、Check按钮对象,直接用下面脚本来实现测试

    JavaWindow("Test").JavaButton("label:=New").Click
    JavaWindow("Test").JavaButton("label:=Modify").Click
    JavaWindow("Test").JavaButton("label:=Delete").Click
    JavaWindow("Test").JavaButton("label:=Check").Click

     二、讲解和举例。

     

       GetToProperty: Returns the value of the specified property from the test object description.

         GetTOProperties: Returns the collection of properties and values used to identify the object.

         GetROProperty: Returns the current value of the test object property from the object in the application.

         举个例子:假设在库中有一个对象"窗口A",用于识别该对象的属性有2个,一个属性是"text",在库中记录的值是"QQQQ".另一个属性是"name",记录的值是"MM".在实际运行脚本时属性"text"的值是"PPPP"而不是"QQQQ".那么:Window("窗口A").GetToProperty("text")返回的是:"QQQQ".Window("窗口A").GetRoProperty("text")返回的是:"PPPP"。Window("窗口A").GetToProperties("text")返回的是用于识别"窗口A"的两个属性和值的集合 . 一个好助手——Object Spy通过Object Spy可以查看到所需要识别的Web对象的所有属性值,挑选其中可以唯一识别该对象的一个属性或多个属性,对该Web对象进行描述。

     
Open Toolbar