记录阿里巴巴QA架构组成长点滴。2008年关键词为效率,技术,影响力!QA/测试架构师定义:开发和设计测试框架测试库;纵横全局的考虑产品的功能,设计复杂的测试系统;负责研发某一项特定的测试技术;为公司考虑如何提高测试效率。领导公司测试技术的发展和测试策略上的方向,关注整个公司的测试部门的问题,前瞻性的考虑未来的版本的测试策略和技术。测试架构师计划/设计测试平台,关注着产品的测试过程,提供咨询服务,影响到公司内的测试机构测试社区,以及开发机构等,对产品各个方面施加深远而正确的影响,最终提高整体软件质量。

发布新日志

  • QTP编码问题

    2008-07-16 13:57:58

    by jiale

    前段时间有个同学问我他在记事本里编辑的QTP代码文件scrīpt.mts,在QTP里打开是乱码了,当时想到之前使用executefile方法执行vbs时出现“非法字符”错误是由于vbs文件非ANSI编码造成的。

    因此尝试将scrīpt.mts用记事本打开,并保存为ANSI编码格式,再用QTP打开,一切OK。

     

  • watir中,类似QTP object spy等方法替代实行

    2008-07-06 23:57:01

    by Wiston Li


    我们知道,QTP提供object spy工具,对象库录制功能,或描述性编程提供给我们快捷识别对象,
    一定程度上可以做到脚本批处理生成。相应的,我在想如此好的工具和技术,为啥watir没有支持?
    也许将来,在自动化同学与watir开发团队努力下,可满足这一需求。

    另一个问题抛出,是否为了准备watir脚本,我们一定要在那么冗长的代码,查找并挑选我们想要操作的对象?
    给出我的简单总结,

    1, 类似object spy工具,为微软为开发人员做的工具ie developer toolbar,可以借助它通过指针指示
    对象,在attribute view中找到相应属性。

    2,类似描述性编程,

    在cmd中运行irb,

    如: puts 2+2
    打印4,可以通过命令实现所见即所得,

    如:
    require ‘watir’
    ie=Watir::IE.start(‘http://localhost:8080’)
    ie.show_all_objects

    结果:

    -----------Objects in  page -------------
    text              name=name           id= 

    如:ie.text_field(:name, 'name').flash
    结果:使操控对象webedit框有黄色闪动效果.


    更有意思的是,当我把鼠标焦点移到某个webedit上,
    然后:
    irb> puts ie.show_active
    text         name=name          
    id=          value=             
    alt=         src=               
    innerText=
    => nil
    在这里,watir将帮助我们标识当前active的对象.
    对于button和其他对象,也可以通过tab key来实现焦点移动,
    再用alt + tab 激活irb窗口,用相同show active方法实现.

    再如:

    irb> puts ie.show_active
    submit              name=submit_logon  
    id=                 value=Login        
    alt=                src=               
    innerText=
    => nil


    irb> ie.text_field(:name, 'name').set(‘Test1’)
    => nil

    可以参照watir api对web edit对象进行内容清除,追加等.
    另外可以输出当前IE对象的HTML

    irb> puts ie.html
    <H1>
    <P align=center>Create the First Job </P></H1>
    <TABLE cellSpacing=0 cellPadding=3 border=0>
    <TBODY>
    <TR>
    <TD align=middle width="40%">
    <FORM id=job name=job action=job method=get><INPUT type=hidden value=22909012 name=session></INPUT>
    <TABLE cellSpacing=0 cellPadding=3 border=0>
    <TBODY>

    或只输出text :

    irb> puts ie.text
    Create the First Job
     Please create the first job

     Make it a background job
    If you make this job a background job, it will be a job that accumulates time when you're not doing any specific task. You'll start it in the morning,
     do your work - starting and pausing other jobs - then stop it when you're done for the day.
    Each time you pause another job, the background job will resume accumulating time.
    If you don't have a background job, you'll have to manage time more explicitly.
    => nil
    irb(main):024:0>


    对于页面中只有一个同特征对象时可以用index, 如:
    ie.text_field(:index, 1).set(‘Testing for Bugs’)

    irb> ie.button(:index,1).click
    => nil

    可以借用show_all_objects方法显示页面中所有对象
    也可以显示某一特定类型对象, 像:


    show_images Show all the images in the document
    show_spans Show all the span tags in the document
    show_labels Show all the labels in the document
    show_links Show all the links in the document
    show_divs Show all the div tags in the document
    show_frames Show all the frames in the document
    show_forms Show all the forms in the document

    如这里用ie.show_table作为示例
    irb> ie.show_tables
    Found 8 tables
    1  id=      rows=1   columns=3
    2  id=      rows=2   columns=1
    3  id=      rows=3   columns=1
    4  id=      rows=2   columns=1
    5  id=      rows=3   columns=1
    6  id=      rows=1   columns=1
    7  id=      rows=3   columns=1
    8  id=recent_records      rows=2   columns=1
    => nil

    我们可用flash方法来标识对象,
    irb> ie.table(:id , ‘recent_records’).flash

    也可以只显示特定table的html code,

    irb> puts ie.table(:id , ‘recent_records’).html

    <TABLE id=recent_records cellSpacing=0 cellPadding=3 width="66%" align=center border=1><TBODY>
    <TR bgColor=#66ffff>
    <TD align=middle colSpan=4>Recent Records </TD></TR>
    <TR bgColor=#ccffff>
    <TD>Testing for Bugs </TD>
    <TD>10:05 PM </TD>
    <TD>0.00 hours </TD>
    <TD><B>running</B> </TD></TR></TBODY></TABLE>

    这里可用ruby内置函数 to_a输出行记录数组,

    irb> irb(main):009:0> my_array = ie.table(:id , 'recent_records').to_a
    => [["Recent Records"], ["Testing for Bugs", "10:05 PM",

    以上可以看到,采用 show_XXX方法,枚举同特征对象,
    是否感觉到类似QTP:create descrīption.../ mic class='web object'/...的方法?

    类似,甚至可以通过修改底层show_XXX代码,来批处理生成脚本,
    如生成已经填值的text field操作脚本,呵呵...

     

     

     

     

     

     

     

     

     

     

     


     

  • 如何破解快速变化的web网站测试自动化困境?

    2008-07-05 19:09:47

      by liangjz

      网站业务复杂度倍增,为了改善由于开发改动一点内容,而QA需要大面积验证相关受影响的模块导致

    工作量剧增的状况,故引入网站自动化测试。

      目前针对网站主干流程核心业务做了粗粒度的验证。技术: qtp + 页面验证 + 数据库验证,业务流

    采用excel管理。 运行了一段时间,发现了一些BUG(自动化目的不是找BUG,而是一种质量保证手段)

    ,但更多的问题也引爆出来.

      经过和微软技术专家的交流,产品线的自动化测试特征如下:

    1) 微软强调单元级的验证粒度非常细致
    2) 微软强调每一个业务模块的数据输入都是全新创建而非利用系统原有数据;自动化退出时环境RESET
     

      我们问题主要集中在几块

    1) 业务经常变更,导致页面元素发生变化,需要及时调整自动化脚本。这个成本居高不下
    2) 自动化采用的脚本是依赖数据库原有的定制化的数据,有时候被QA测试时修改,干扰脚本运行。
    但如果全新创建,需要跨别的系统且需要人工审核的环节,这个成本也很高昂
    3) 目前的验证点粒度比较少, 但如果页面验证点颗粒很细,大多数时候页面元素不发生变化,这种验

    证效用大么?验证点增加也会带来脚本运行速度下降,维护成本增强等问题
    4) 自动化测试脚本主要用于项目发布前的回归验证。没有用到项目测试中,原因有需要更换一批新的数

    据、脚本需要更快速和业务变化同步等

     不知道其他业务型的互联网公司如何运作,让自动化脚本最大化发挥功效的?

     目前开发尚无单元测试代码,无法做到daily test。而开发没有清晰的API接口说明,开发和测试的工

    作边界不够清晰导致测试无法写单元测试。


     

  • 自动化中特殊对象的变通处理

    2008-07-03 08:47:41

    by jiale

    在做自动化项目中碰到了怪异的对象,它是一个第三方提供的控件,被QTP识别为winobject,对操作进行录制结果是click了控件上的一个坐标:Window("").Window("").Page("").Frame("Frame").WinObject("ActiveX").Click 388,9,显然由于坐标受到页面大小的影响,成功回放几乎变成不可能,这有什么办法可以解决呢,我们发现可以使用tab键使控件得到焦点,于是想到了使用SendKeys,首先定位到可识别的对象使其获得焦点,然后SendKeys tab键使光标移动到需要输入的控件,再用SendKeys输入想输入的字符,ok,一切变得简单了
     好了,下面是SendKeys的使用方法
    Dim oWinApp
    Set ōWinApp=createobject("Wscrīpt.Shell")
    oWinApp.sendkeys InputText
    Set ōWinApp = nothing

     键盘给特殊键对应的入参
    Key   Argument
    BACKSPACE  {BACKSPACE}, {BS}, or {BKSP}
    BREAK   {BREAK}
    CAPS   LOCK {CAPSLOCK}
    DEL or DELETE  {DELETE} or {DEL}
    DOWN ARROW  {DOWN}
    END   {END}
    ENTER   {ENTER} or ~
    ESC   {ESC}
    HELP   {HELP}
    HOME   {HOME}
    INS or INSERT  {INSERT} or {INS}
    LEFT ARROW  {LEFT}
    NUM LOCK  {NUMLOCK}
    PAGE DOWN  {PGDN}
    PAGE UP  {PGUP}
    PRINT SCREEN  {PRTSC}
    RIGHT ARROW  {RIGHT}
    SCROLL LOCK  {SCROLLLOCK}
    TAB   {TAB}
    UP ARROW  {UP}
    F1   {F1}
    F2   {F2}
    F3   {F3}
    F4   {F4}
    F5   {F5}
    F6   {F6}
    F7   {F7}
    F8   {F8}
    F9   {F9}
    F10   {F10}
    F11   {F11}
    F12   {F12}
    F13   {F13}
    F14   {F14}
    F15   {F15}
    F16   {F16}


     

  • [论坛] 获取页面上所有指定属性的对象

    2008-06-17 17:00:45

    by jack

    在QTP脚本编写的时候,我们可能会遇到这种检查点:比如获取checkbox的个数等等,这时会提出“获取页面上所有指定属性的对象”的需求。下面是用descrīption对象实现的一个函数,作用就是实现上述需求。

    Function getItemList(PageObject,PropertyName,PropertyValue)
     Dim oItemDesc
     Dim n

     Set ōItemDesc=descrīption.Create

     If isarray(PropertyName) and isarray(PropertyValue) Then
      Dim iCountPropertyName
      Dim iCountPropertyValue
      
      iCountPropertyName = ubound(PropertyName)
      iCountPropertyValue = ubound(PropertyValue)

      If iCountPropertyName <= iCountPropertyValue Then
       For n=0 to ubound(PropertyName)
        oItemDesc(PropertyName(n)).value=PropertyValue(n)
       Next
      Else
       'lost property value
       'msgbox "lost property value"
       Exit Function
      End If
      
     Else If (not isarray(PropertyName)) and (not isarray(PropertyValue)) Then
       oItemDesc(PropertyName).value=PropertyValue
      Else
       'error
       'msgbox "error"
       Exit Function
      End If
     End If

     Set getItemList=PageObject.childobjects(oItemDesc)

    End Function

    输入参数有3个,page对象,属性名,属性值;其中属性名和属性值可以为数组,应用举例:

    '取出页面所有编辑框

    set ōChildList = getItemList(page("51Testing软件测试网"),"micclass","WebEdit")

    '编辑框数量

    iCountChildList = oChildList.count

    '取出页面所有name含有“测试”的链接


    set ōChildList = getItemList(page("51Testing软件测试网"),array("micclass","name"),array("Link","测试.*"))

    '点击第二个链接

    oChildList(1).click


     

  • 对象识别怪现象

    2008-06-06 09:34:52

    by jiale

             最近在做中文站自动化脚本时,碰到了一个奇怪的现象,是一个image对象,用qtp的object spy获取这个对象的filename属性用来识别它,但回放的时候总是提示无法识别该对象,filename属性是可以用来作为识别image对象的唯一属性的,而且这个页面也只有一个image对象,相当的奇怪呀,多次用object spy抓取时发现,抓取前与抓取后的filename是不一致的,赶紧打开源代码看看缘由,原来该对象的onmouse事件将这个对象的图片source给改变了,当鼠标移到该对象时,改变图片A为图片B,因此object spy抓取的filename属性是图片B,而回放时并没有onmouse为图片A,当然用图片B作为识别属性就无法识别该对象,只有用(filename:=图片A)才能正常识别。

  • 使用ADO查询Oracle中文乱码问题

    2008-06-06 09:34:20

    by jiale

             QTP做自动化脚本,用MS ADO对象操作数据库的时候,发现一个奇怪的问题,查询中带有中文时如:select count(*) from xxx where xxx like '中文%',查询失败,有数据也返回0,同样insert语句插入中文时乱码,初步判断是客户端字符集的问题,当前使用的客户端字符集与服务器端不匹配,select * from V$NLS_PARAMETERS WHERE parameter =  'NLS_CHARACTERSET'查询,发现服务器端为US7ASCII,查看客户端注册表HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE下NLS_LANG键值为NA,而HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE\HOME0下NLS_LANG键值为AMERICAN_AMERICA.us7ascii,统一修改为AMERICAN_AMERICA.us7ascii,以为问题解决,可是查询仍然失败。
             后来想到我们使用前面讲到的OraOleDb.Oracle字串链接数据库,那是不是OraOleDb.Oracle与MS的ADO配合有问题呢?我们将OraOleDb.Oracle改成MS的MSDAORA,查询一切ok。
             因此这个中文乱码是两个错误的叠加,1、客户端没有使用正确的字符集——修改注册表ORACLE字符集,2、ADO的数据库连接没有使用正确的容器——MS的ADO就使用MS提供的MSDAORA数据库连接。

  • 利用wshell.exec方法执行命令信息交互

    2008-06-04 20:57:15

    By Wiston Li

    自动化通常碰到cmd执行窗口,可以用qtp的操作对象的方法控制窗口,并取到命令执行后返回的文本,

    如:

    Syntax : object.GetVisibleText ([Left], [Top], [Right], [Bottom])

    但当命令返回多于一屏时,此命令就不行了,可以用下面的替代方法来解决:

    Option Explicit
    Const SystemFolder = 1
    Dim wShell, exec, fso
    Dim dirList
    Set wShell = CreateObject( "Wscrīpt.Shell" )
    Set fso = CreateObject("scrīpting.FileSystemObject")
    dirList = fso.GetSpecialFolder( SystemFolder ) & "\*.exe"
    Set exec = wShell.Exec( "%comspec% /C dir " & dirList & " /B /O-N /L" )
    Do While True 
      If Not exec.StdOut.AtEndOfStream Then  
          dirList = exec.StdOut.ReadAll   
         If exec.ExitCode = 1 then      
               Reporter.ReportEvent micWarning, "Command failed", dirList    
               End If    
             Exit Do 
              End If  
           If Not exec.StdErr.AtEndOfStream Then  
               dirList = "STDERR: " & exec.StdErr.ReadAll  
                  Reporter.ReportEvent micFail, "Command failed", dirList  
                  Exit Do  
               End If  
               Wait 1
               Loop
               Print dirList

     


     

  • 解压缩文件到目标目录

    2008-06-04 20:51:47

    by Wiston Li

    解压文件 
    Dim sZIPFile 
    Dim sExtractToPath 
    Dim oShell  
    Dim oZippedFiles 

    sZIPFile="d:\bid.zip" 
    sExtractToPath="d:\temp"
    Set ōShell = CreateObject("Shell.Application")  
    '取到zip包中的内容 
    Set ōZippedFiles=oShell.NameSpace(sZIPFile).items 
    ' 释放到目标目录
    oShell.NameSpace(sExtractToPath).CopyHere(oZippedFiles) 
    ' 释放对象 
    Set ōZippedFiles = Nothing
    Set ōShell = Nothing

  • 压缩文件到某个zip文件中

    2008-06-04 20:49:07

    by Wiston Li

    通常,为了传送与保存大文件时,可以考虑打包到zip的方法,

     '变量定义
     Dim sSourceFolder
     Dim sArchiveFile
     

     Dim oShell  
     Dim oZIP  
     Dim oSourceFolder 
       
    '初始化

    sSourceFolder = "d:\bid"
     sArchiveFile = "d:\bid.zip" 
    set ōShell = CreateObject("Shell.Application") 
     '建立zip对象,namespace是oshell内置对象 
    Set ōZIP= oShell.NameSpace(sArchiveFile)
    '得到源目录
    Set ōSourceFolder=oShell.NameSpace(sSourceFolder)

     '加文件到zip包中。
     oZIP.CopyHere(oSourceFolder.Items)

  • 用CDO对象发邮件

    2008-06-04 20:44:21

    By wiston Li

    今天,开始和大家分享一下,基于windows scrīpting tips,

    此类小知识,可能在大家准备自动化脚本时碰到,有些windows开放的com对象,应用到自动化测试中

    特别时调用com对象内置的方法与属性,能起到事半功倍的作用。

    在这里整理一下:

    Function sendmailbysmtp(Mailto, FilePath)
    Dim objEmail
    Set ōbjEmail = CreateObject("CDO.Message")
    objEmail.From = "B2bTA@b2btest.com"
    objEmail.To = Mailto
    objEmail.Subject = "This is an email sent by TA"
    objEmail.Textbody = "Pls see the enclosed for the TA execution log"

    objEmail.AddAttachment (FilePath)
    objEmail.Configuration.Fields.Item _
        ("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
    objEmail.Configuration.Fields.Item _
        ("http://schemas.microsoft.com/cdo/configuration/smtpserver") = _
            "10.0.32.124"
    objEmail.Configuration.Fields.Item _
        ("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
    objEmail.Configuration.Fields.Update
    objEmail.Send

     
    End Function

    这个函数,也是我们现在自动化框架用到的发邮件,

    另外,也除了发送txt文本格式的邮件,也可以发送基于html格式的,详见:

    Set ōbjMessage = CreateObject("CDO.Message")
    objMessage.Subject = "Example CDO Message"
    objMessage.From = "wiston.lifb@alibaba-inc.com"
    objMessage.To = "wiston.lifb@alibaba-inc.com" objMessage.CreateMHTMLBody "file://d|/temp/test.htm" ' 把d:/temp/test.htm作为邮件内容发送。
    objMessage.Bcc = "wiston.lifb@alibaba-inc.com"
    objMessage.Cc = "wiston.lifb@alibaba-inc.com"
    objMessage.Send

     

  • excel对象池(二)

    2008-05-15 18:09:01

       by jiale
      

       完成对象数据池的实现后,又遇到了一个问题,我们是在代码里手工释放对象池的,但当脚本遇到对象无法识别,qtp自动启动场景恢复重启脚本时,我们是否对象池的语句大部分情况不能被执行,因此会导致excel进程一直驻留,所以就需要在启用场景恢复时杀掉所有的excel进程,幸好qtp的场景恢复提供了在场景恢复时可以执行外部vbs的某个方法,这样我们将杀excel进程动作就放到vbs的某个方法中完成。
       下面的脚本利用WMI杀excel进程:
    Function KillExcel
                    dim oCIMV2
                    dim oExelEXE
                    dim i
                    Set oCIMV2=getobject("winmgmts:\\.\root\cimv2")
     Set ōEXCELEXE=oCIMV2.execquery("select * from win32_process where name='EXCEL.EXE'")
     For Each i In oEXCELEXE
      i.terminate()
     next
     
     Set oCIMV2 = nothing
     Set ōEXCELEXE = nothing
    End Function

  • excel对象池

    2008-05-15 12:36:37

       by jiale 

       自动化框架遇到了脚本执行时间长的问题,也就有了性能优化的需求,我们分析了影响性能的几个方面,其中一个是由于我们框架数据池是使用excel实现,每次读写数据池参数都要打开关闭excel.exe,打开excel.exe的时间就需要1-2秒,大大的影响性能,因此我们准备引入excel对象池,相同的excel文件只打开一次,打开后一直驻留在内存中,直到完成全部对该excel文件读写后关闭,这样读写数据池参数时不做打开关闭excel的动作,而改为读取excel对象池,打开关闭excel都有对象池方法来完成,

       对象池方法:

    一、从对象池获取excel对象:在对象池中查找是否存在该excel文件excelworkbook对象,存在则返回excelworkbook对象,不存在则打开文件后返excelworkbook对象

    Function xlsGetPoolWorkbook(path)
     Dim i
     Dim oTempWorkBook
     If IsEmpty(oPoolExcelApp) Then
      Set ōPoolExcelApp = xlsCreateExcel()
     End If
     '对象池中存在则返回
     For i = 0 To iPoolMaxNumber
      If sPoolFilePath(i) = path Then
       Set xlsGetPoolWorkbook = oPoolWorkBook(i)
       Exit Function
      End If
     Next
     '对象池中不存在则打开
     On Error Resume Next 
     Set ōTempWorkBook = xlsOpenWorkbook(oPoolExcelApp,path)
     If Err.Number <> 0 Then
      Set xlsGetPoolWorkbook = nothing
     Else
      iPoolMaxNumber = iPoolMaxNumber + 1
      ReDim oPoolWorkBook(iPoolMaxNumber)
      ReDim sPoolFilePath(iPoolMaxNumber)
      Set oPoolWorkBook(iPoolMaxNumber) = oTempWorkBook
      sPoolFilePath(iPoolMaxNumber) = path
      oTempWorkBook = nothing
      Set xlsGetPoolWorkbook = oPoolWorkBook(iPoolMaxNumber)
     End If
    End Function

    二、释放对象池中某个excel对象

    Function xlsFreePoolWorkbook(path)
     Dim i
     If IsEmpty(oPoolExcelApp) Then
      xlsFreePoolWorkbook = 1
      Exit Function
     End If
     '对象池中存在则释放
     For i = 0 To iPoolMaxNumber
      If sPoolFilePath(i) = path Then
       Set oPoolWorkBook(i) = nothing
       xlsFreePoolWorkbook = 1
       Exit Function
      End If
     Next
    End Function

    三、释放对象池中所有excel对象

    Function xlsFreeAllPoolWorkbook()
     Dim i
     If IsEmpty(oPoolExcelApp) Then
      xlsFreeAllPoolWorkbook = 1
      Exit Function
     End If
     For i = 0 To iPoolMaxNumber
      Set oPoolWorkBook(i) = nothing
      sPoolFilePath(i) = ""
     Next
     iPoolMaxNumber = -1
     oPoolExcelApp.quit
     Set ōPoolExcelApp = nothing
     xlsFreeAllPoolWorkbook = 1
    End Function

  • 用字典对象实现堆栈的数据结构

    2008-05-12 20:32:57

    By Wiston Li

    堆栈的数据结构的特征是Last in First out, 并被广泛应用,如错误的保护于处理,后出现的问题先处理,直到所有错误处理完毕。

    下面一个程序采用字典对象实现堆栈, 用class 实现:

     

    Class clsStack  '定义堆栈

       Private m_stack ' Dictionary Private member

       ' ** Class Constructor

       Private Sub Class_Initialize  

          Set m_stack = CreateObject( "scrīpting.Dictionary" )

          m_stack.Add "next", 0

       End Sub

       ' ** Class Destructor

       Private Sub Class_Terminate  

          If m_stack.Count > 0 Then

             m_stack.RemoveAll

          End If

          Set m_stack = Nothing

       End Sub       ' 初始化变量与函数

     

      

    Public Property Get Count() ' 定义count方法

       Count = CLng( m_stack( "next" ) )

    End Property

     

    Public Property Get IsEmpty() ' 定义isEmpty方法

       IsEmpty = CBool( Me.Count = 0 )

    End Property

     

    Public Sub Push( ByVal data ) ' 定义push方法

       m_stack.Item( m_stack.Item( "next" ) ) = data

       m_stack.Item( "next" ) = CLng( m_stack.Item( "next" ) ) + 1

    End Sub

     

    Public Function Pop()' 定义pop方法

       If Me.IsEmpty Then

          Reporter.ReportEvent micFail, "Stack", "The Stack is empty"

          Pop = Empty

       End If

       Pop = m_stack.Item( m_stack.Item( "next" ) - 1 )

       m_stack.Remove m_stack.Item( "next" ) - 1

       m_stack.Item( "next" ) = CLng( m_stack.Item( "next" ) ) - 1

    End Function

     

     

    Public Function Peek() ' 定义peek方法

       If Me.IsEmpty Then

          Reporter.ReportEvent micFail, "Stack", "The stack is empty"

          Peek = Empty

       End If

       Peek = m_stack.Item( m_stack.Item( "next" ) - 1 )

    End Function

     

    Public Sub Clean() ' 定义clean方法

       If Not Me.IsEmpty Then

          m_stack.RemoveAll

          m_stack.Add "next", 0

       End If

    End Sub

    Public Function Clone()' 定义clone方法

       Dim m_cloned, key

       Set m_cloned = CreateObject( "scrīpting.Dictionary" )

       For Each key in m_stack.Keys

          m_cloned.Add key, m_stack( key )

       Next

       Set Clone = m_cloned

    End Function

    Public Function ToArray()' 定义ToArray方法

       Dim m_cloned

      

       Set m_cloned = Me.Clone()

       m_cloned.Remove( "curr" )

       m_cloned.Remove( "next" )

       ToArray = m_cloned.Items

    End Function

    End Class

     

    ' How it works?

    Set stack = New clsStack

    Print "stack.Count : " & stack.Count     ' Prints zero

    Print "stack.IsEmpty : " & stack.IsEmpty ' Prints True

    ' ** Adding a value

    stack.Push "Message 1″

    Print "stack.Count : " & stack.Count       ' Prints 2

    Print "stack.IsEmpty : " & stack.IsEmpty   ' Prints False

    Print "stack.Peek : " & queue.Peek()       ' Prints "Message 2″

    Print arr = stack.ToArray()

    Do While stack.IsEmpty = False

       Print stack.Pop()

    Loop

    Set stack = Nothing

     

     

  • excel采用usedRanage 与jet用select实现单元格访问

    2008-05-12 20:08:18

    By Wiston Li

    在访问excel时,可用如下两种机制来进行单元格的访问遍历,

    1,excel采用usedRanage 遍历到二维数组  2,采用jet用select into 到变量中去

    二者都是放在内存中,从这两者的实现思路可以看到提高excel的i/o性能的可能着手地方,

    代码如下:

    1,
    tmpArray = ReadExcel  ("d:\devtmp\ADOtest.xls", "join")

    For i= 1 to ubound(tmpArray, 1)     ' 输出二维数组
      For j = 1 to ubound(tmpArray, 2)
         msgbox tmp(i,j)
       Next
    Next

    Function ReadExcel(sFileName,sSheetName)
    Dim oExcel
    Dim oRange
    Dim arrRange

    On Error Resume Next
    Set ōExcel = CreateObject("Excel.Application")
     oExcel.Workbooks.Open(sFileName)
     Set ōRange = oExcel.Worksheets(sSheetName).UsedRange ' 输出到二维数组中oRange
     If Err.Number <> 0 Then
     ReadExcel = Array("Error")
     msgbox   ReadExcel
     Exit Function
     End If
    On Error Goto 0
     
     arrRange = oRange.Value 
     Set ōRange = Nothing
     oExcel.Quit
     Set ōExcel = Nothing
     ReadExcel = arrRange
     
     End Function

     

    2, Function xlsGetSpecifiedDataFromDPForParameterbak(sFilePath,  sSheetName, sVarible )

          Dim sConnStr                                     
          Dim oConn                                        
          Dim oRS                                          
          Dim oFSO  
          Dim sSql
          Dim iFirstEnd
          Dim iSecondEnd                                      
          On Error Resume Next
          Set ōFSO = CreateObject("scrīpting.FileSystemObject") ' check the file if exists
         
          If  not  oFSO.FileExists(Trim(sFilePath)) Then   
              MsgBox "No Datapool File Found"
              Exit Function
          End If 
          If Trim(sSheetName)= "" Then  ' check the worksheet if exists
             MsgBox "No Sheet Found"
             Exit Function
          End If
         
          ' create the jet connection and get the DB object
          sConnStr   =  "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" &sFilePath  & ";Extended Properties=""Excel 8.0;HDR=Yes;IMEX=1""; "
          Set   ōConn =   CreateObject("ADODB.Connection")  
          oConn.Open   sConnStr
          Set ōRS = CreateObject("ADODB.Recordset")
          iFirstEnd=4   ' mark the start flag
          iSecondEnd=1000 ' mark the end flag, but unlimit depend on your requirement
          sSql="Select * from [" &sSheetName & "$A" &iFirstEnd  &":B" & iSecondEnd  &"]"
         
          ' find the data you want to search
          oRS.Open sSql,  oConn
          If Trim(sVarible) <> ""  Then 
            Do   While   Not   oRS.EOF   
                      If Trim(oRS(0)) = "&end&" Then  ' terminal the find until end
                       Exit Do
                      End If
                      
              If trim (oRS(0)) =trim (sVarible) Then
                    xlsGetSpecifiedDataFromDPForParameter=oRS(1)
                    Exit function
              End If
                oRS.MoveNext   
            Loop 
            Else 
              MsgBox "No data matched"            
          End If 
           
          Set ōConn = nothing
          Set ōRS = nothing
          Set ōFSO = nothing
          If Err.Number <> 0 Then                                                                         
             MsgBox ("Error # " & CStr(Err.Number) & " " & Err.Descrīption & Err.Source)
          End If
          On Error Goto 0
    End Function


     

  • 类似ruby中hash table数据结构在QTP中的应用

    2008-05-12 17:08:14

    by wiston

    计算机在处理数据时,很重要的数据结构类型,如: 顺序表(数组,队列,链表,堆栈),二叉树,图与hash表等,在vbs programming时大家经常用到顺序表如数组等,今天介绍一下,QTP vbs中类似hash结构的字典对象的处理

    1, 初始化:

    Set ōSettings = CreateObject("scrīpting.Dictionary")
        oSettings.Add "10.0.32.124", "b2btest.com" 
        oSettings.Add "10.0.32.123", "b2bqa.com" 

    2, 遍历:

    skeys  = oSettings.keys ' 取到关键词集合
    sitems = oSettings.items ' 取到值集合
    For i = 0 to oSettings.count -1

     msgbox   skeys(i) & "  "  & sitems(i)  ' 输出

    Next

    3, 删除:

      oSettings.Remove("10.0.32.123")

    4, 判断:

       If oSettings.Exists("10.0.32.123") Then
          msgbox  "Specified key exists."
       Else
          msgbox "Specified key doesn't exist."
       oSettings.Add "10.0.32.123", "b2bqa.com"
       End If

    上面的数据结构非常实用,比如:与hosts文件交互,把此hash结构中key与value值,自动化去访问文件

    即可以解决需要手工去配置hosts中域名绑定,而实现自动化无人职守方法之一。

  • 无人值守运行自动化脚本

    2008-05-12 14:00:02

       by jiale

       脚本越来越多,需要运行的场景越来越多,运行时间越来越长,需要无人值守的运行自动化脚本,并能在报告中体现运行情况,对于我们的自动化脚本框架业务驱动的实质,mainaction用callaction的方法调用子action,子action中的错误是不能在mainaction中用On Error的方法捕捉,自然当子action出现对象无法识别等异常时mainaction不能做出容错处理,程序自动stop,需要人工干预。

       这是个无法回避的问题,这时我们想到了使用QTP的场景恢复功能,他能够识别到子action的对象无法识别等异常,并重启mainaction,那我们只需要告诉mainaction从哪里开始继续执行下去就可以了,之前已经运行的场景、action就不需要再运行,这时又出现一个问题,是从场景运行,或者从哪个组成场景的子action运行,首先如果从同一个场景的子action往下运行,有可能前一个错误的action是下一个action的必要条件,那么继续运行没有意义,而我们设置的场景却是相对独立的,且出错场景的再次运行出错的概率会很大,因此我们让mainaction从出错场景的下一个场景开始运行,有了这个思路,程序的实现就相对容易了,只需要在本地文件中记录当前正在运行的场景号,一旦出错,QTP场景恢复重启mainaction后,读取该文件的场景号,然后从场景号+1的场景继续运行,直到结束。

        值得注意的是,当然每个场景开始运行需要打印开始标志,结束时打印结束标志,以得到该场景是否成功完成运行的信息。

        目前这个方案实施的效果不错,但QTP的场景恢复无法识别vbs的语法错误,一旦有vbs语法错误仍然无法做到无人值守,因此子action的编码就需要有一定的质量保证。

  • ADODB.Connection使用OraOLEDB.OracleOracle字串链接数据库

    2008-04-14 09:35:52

    我们自动化脚本很多时候需要链接数据库进行数据验证或者数据准备,一般是用ADODB.Connection对象的open方法获取数据库链接,open方法的链接字串可以有很多种的形式,如:使用odbc、OraOLEDB.OracleOracle,但我们需要只需读取某个配置文件中的host、port、sid、userid、password就可以获得adobd的数据库链接,而不想在运行脚本的每台机器单独配置odbc或者oracle的tnsnames.ora,我们可以用OraOLEDB.OracleOracle字串获取链接adodb的数据库链接

    strHost=从配置文件获取

    strPort=从配置文件获取

    strSID=从配置文件获取

    strUser=从配置文件获取

    strPassword=从配置文件获取

    strConnectionString = "Provider=OraOLEDB.Oracle;Persist Security Info=True;"&_
            "Data Source=(DEscrīptION =(ADDRESS_LIST =(ADDRESS = (PROTOCOL = TCP)(HOST ="+strHost +")(PORT = "+strPort+")))"&_
            "(CONNECT_DATA =(SID = "+strSID+")));User ID="+strUser+";Password="+strPassword+";"

    Set ōConnection= createobject("ADODB.Connection")

    oConnection.ConnectionString = strConnectionString
    On error Resume Next 
    oConnection.open
    If err.Number <> 0 Then
     msgbox "数据库连接失败"
    End

  • 如何得到localmachine的odbc driver安装配置?

    2008-04-08 14:31:20

     

    同样,在获取odbc driver时,也可以用下面方法来实现:

    Const HKLM = &H80000002
    Set reg = GetObject( "winmgmts:\\.\root\default:StdRegProv" )
    keyPath = "SOFTWARE\ODBC\ODBCINST.INI\ODBC Drivers"
    reg.EnumValues HKLM, keyPath, valueNamesArr, valueTypeArr
     For i = 0 to UBound( valueNamesArr )
       reg.GetStringValue HKLM, keyPath, valueNamesArr( i ), valueStr   
        Print valueNamesArr( i ) & " –> " & valueStr
     Next

    打印显示条目,具体可以参照附件。

  • 如何得到odbc数据源中系统DSN的安装配置?

    2008-04-08 14:22:28

     

    上个月在研究框架excel遍历提速时,起初设想通过dsn对象来访问excel,

    但需要遍历操作系统中是否安装excel driver和是否已经存在dsn, 但迫于这种方案比较土。后来没有这么做而是采用jet对象直接动态访问excel数据源. 其间在实现上述提到的方案,后来找到相关实现方法, 没有用到任何wmi和com对象,只是遍历与访问注册表。

    Const HKLM = &H80000002 '定义常量
     Set reg = GetObject( "winmgmts:\\.\root\default:StdRegProv" )
     keyPath = "SOFTWARE\ODBC\ODBC.INI\ODBC DATA SOURCES" ' 欲取注册表分支
     reg.EnumValues HKLM, keyPath, valueNamesArr, valueTypeArr  '前面两个参数是传入,后面两个是传出参数, 均为数组。


     For i = 0 to UBound( valueNamesArr )  
       reg.GetStringValue HKLM, keyPath, valueNamesArr( i ), valueStr      
        Print valueNamesArr( i ) & " –> " & valueStr
        Next

492/3<123>
Open Toolbar