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

发布新日志

  • 前端web分析工具pagetest核心技术

    2008-06-02 00:27:20

    pagetest分析web 页面解析时间。PageTest为AOL 开放源码的,
    可从http://pagetest.wiki.sourceforge.net/ 下载。

    作用同ibm pagedetailer ,yahoo yslow。

    pagetest用visual .net工程。

    核心技术
    1 Winsock2 SPI
    参考
    http://www.microsoft.com/msj/0599/LayeredService/LayeredService.aspx
    http://www.vckbase.com/document/viewdoc/?id=643
    或者windows 网络编程第14章winsock2服务提供者接口
    如加载 SPI
    wspStartup = new CAPIHook("mswsock.dll", "WSPStartup", (PROC)::WSPStartup_Hook, TRUE);  
       nspStartup = new CAPIHook("mswsock.dll", "NSPStartup", (PROC)::NSPStartup_Hook, TRUE); 

    主要函数如:
    NSPLookupServiceBegin;NSPLookupServiceNext;NSPLookupServiceEnd
    WSPSocket,WSPBind

    2 WinInet

    internetOpen

    3  IE插件技术

    class ATL_NO_VTABLE CIEHook :
     public IObjectWithSiteImpl<CIEHook>,
     public IOleCommandTarget,
     public IDispEventImpl<1, CIEHook, &DIID_DWebBrowserEvents2, &LIBID_SHDocVw, 1, 1>,
        public IIEHook

    STDMETHOD_(void,OnBeforeNavigate2)( IDispatch *pDisp, VARIANT * url, VARIANT * Flags, VARIANT * TargetFrameName, VARIANT * PostData, VARIANT * Headers, VARIANT_BOOL * Cancel );
     STDMETHOD_(void,OnDocumentComplete)( IDispatch *pDisp, VARIANT * url );
     STDMETHOD_(void,OnDownloadBegin)( VOID );
     STDMETHOD_(void,OnDownloadComplete)( VOID );
     STDMETHOD_(void,OnNavigateComplete)( IDispatch *pDisp, VARIANT * url );
     STDMETHOD_(void,OnNavigateError)( IDispatch *pDisp, VARIANT *url, VARIANT *TargetFrameName, VARIANT *StatusCode, VARIANT_BOOL *Cancel);
     STDMETHOD_(void,OnNewWindow)( IDispatch ** pDisp, VARIANT_BOOL *cancel );
     STDMETHOD_(void,OnQuit)( VOID );

     // IOleObjectWithSite Methods
     STDMETHOD(SetSite)(IUnknown *pUnkSite);


    然后在IE浏览器初始化时做
    void CIEHook::InstallHooks(void)
    {
     // load ourselves to make sure we stay loaded until the browser goes away
     // otherwise some of the API hooks will crash
     LoadLibrary(_T("Pagetest.dll"));

     // hook the browser wndProc (to supress crashes)
     #ifndef DEBUG
     if( !dispatch_hook )
      dispatch_hook = new CAPIHook("user32.dll", "DispatchMessageW", (PROC)::DispatchMessageW_hook, TRUE);
     #endif
     
     // hook winsock
     WinsockInstallHooks();
     
     // hook wininet
     WinInetInstallHooks();
    }

    4 windows精确计时

             EnterCriticalSection(&cs);
      QueryPerformanceCounter((LARGE_INTEGER *)&lastActivity);
      LeaveCriticalSection(&cs);

    5 各个环节的衔接
      这个才是重中之重。

  • 遴选合适岗位人才要领

    2008-05-27 08:06:16

     

    一 分析现有岗位人员技能特点,最好增加互补型人才

      假如现有的人才都属于通才型,那需要找到一个很有专长的人才,作为互补最合适。
    还有如果团队都是年轻人,加入一个经验丰富稳重一些的更好

    二 考察专业能力
     
      工程师考察专业能力相对容易一些,可以将自己经历过的项目提炼出一些有梯度的问题,判断应聘者

    属于需要人带 、独立做事情、带团队做事情、某个领域的专家中哪种类型

      专业能力最好能涵盖基础知识以及项目实践,把握好深度、广度。基础好的人,发展潜力、爆发力比

    较好

    三 考察学习能力

     了解碰到疑难问题的解决模式,以及对这个问题是否有提问的智慧。

    另外,可以将业余爱好与学习能力结合起来。还有询问经常去的一些网站,记得哪些比较深刻的技术细

    节。
     
      一般情况下,如果对某个比较难的技术问题把握得很到位,也可以侧面了解到学习能力高下。
      学习意愿可以从他最擅长的知识入手考察,一般意愿强的人,喜欢寻根究底,深刻理解背后的原理。
    反面而言,就是一根筋:)

    四 考察适应环境能力
     
     可以从几个方面看
    1 承受压力能力:互联网公司项目多\紧急,一般面临多项工作并行开展,同时节凑快。
    2  软技能:测试工作繁复琐碎,需要耐心、细致的特质;由于需要频繁和外部联系,良好的沟通技巧,

    有利于顺利开展工作。开放、共享的心态有助自我提升。
     
      还需要判断其缺点是否影响其业绩

    3  团队的融入性: 做事风格不能相差太多。认可结果为导向、客户第一等多项价值观

    五 职业素质
     
    1 比如跳槽是否过于频繁,能否有稳定性
    2 对自己是否有一个清晰的定位以及职业发展规划

     实际上,在挑选高级人才时碰到最大的问题有几个
    A) 比较接近的候选人偏少
    B)  把握不准到底适合这个岗位、流入市场的候选人到底有多少?这里有类似非常复杂的猴子捡玉米问


    C)  在软技能和潜力把握方面难度比较大

    欢迎大家探讨

  • iconv文件编码转换(linux)

    2008-05-26 09:24:21

    by jiale 

      研究QTP二进制配置文件数据结构时,希望将二进制文件转换成可看懂的文本,这种转换可以用linux下iconv命令实现,当然iconv命令还可以实现各种编码文件间的转换

    格式:iconv 源文件 -f 当前编码 -t 转换后编码 -c >输出文件
    如: iconv test.tsp -f ucs-2 -t gbk -c >test.txt

    iconv -l:查看已支持的编码

  • web软件测试难题

    2008-05-25 15:46:40

    最近在整理测试难题,以及打算在跨子公司讨论、分享的话题。呵呵,大致列举一些

    1 如何清晰度量产品的测试质量?

       按测试覆盖率?按BUG遗漏数?按已经发现BUG的曲线图?哪些标准度量最合适

    2 如何为复杂产品/大型测试项目选取测试策略? 如
     
    镜像站点测试
    异地数据同步测试
    重构项目测试

    3 支持多浏览器(IE6/ie7/firefox...)/多OS软件如何测试?

    降低成本的测试方法有哪些?

    正交表测试方法满足我们的需求么?

    4 支持国际化语言版本的软件如何测试?如国际站网站支持英文,繁体版,马来西亚语言。
    降低成本的测试方法有哪些?

    5 如何在时间、进度压力下,最优选取测试集合?
    回归测试的面积多大算合理?

    6 如何提高估计测试时间的准确度?

    7 跨部门、跨公司的接口测试如何开展,以提高协调效率?

    如中文站和阿里软件贸易通状态接口,国际站和后台CRM 接口,

    8 如何有效度量测试工程师的绩效?

    9 生产环境依赖于外部昂贵的设备,在测试环境开展性能测试如何模拟?

    比如专用邮件服务器,图片服务器?

    10 生产环境的数据量巨大,如何剪裁合适的数据集作为性能测试基准数据?

    11 如何更快找到合适的测试人才?

    12 如何提高开发、测试双方的满意度?

    13 如何在没有单元测试代码情况下,度量代码测试覆盖率

    14 现有的软件测试平台更适合传统的大型软件测试,能否、如何定制更适合快速上线的WEB系统?

    如QC 的报表、需求管理2部分功能一直没有采用。

    15 项目管理、需求管理缺陷管理多个系统入口, 并没有统一关联。另外代码与需求之间映射关系随着业务变更也难以一一映射

    ...待续

  • expect模拟伪终端来实现免人机交互_by_wxc

    2008-05-21 12:07:07

    最近遇到一个小问题,自动化脚本需要在各普通用户间su来su去,渴望非人际交互,瞬间想到的方法是通过重定向、pipe来解决。但是su不同于passwd程序,它的逻辑比较简单,没有一些额外的参数入口来提供重定向和pipe。它必须要从终端上得到用户的输入,所以不可避免的要人际交互。那么可以用什么方法来实现自动输入密码吗?答案就是expect,它可以模拟一个伪终端程序骗过系统,实现非人际交互。(不过密码是明文的,适合于没有安全性要求的自动化),基本代码如下:

    #!/usr/local/bin/expect -f
    spawn su 用户1           //启动一个su的进程
    expect "Password:*"     //等待文本Password:,有点类似LD里的捕获检查点
    sleep 1                 //沉睡一秒
    send "用户1对应的密码\r"  //发送密码给进程
    expect eof              //结束

    转贴一段:现代的Shell对程序提供了最小限度的控制(开始,停止,等等),而把交互的特性留给了用户。 这意味着有些程序, 你不能非交互的运行,比如说passwd。 有一些程序可以非交互的运行,但在很大程度上丧失了灵活性,比如说fsck。 这表明Unix的工具构造逻辑开始出现问题。expect恰恰填补了其中的一些裂痕,解决了在Unix环境中长期存在着的一些 问题。 expect使用Tcl作为语言核心。不仅如此,不管程序是交互和还是非交互的,expect都能运用。这是一个小语言和Unix的其他工具配合起来产生强大功能的经典例子。

  • 活下来,还要活下去

    2008-05-19 15:24:07

  • c++如何调用java程序

    2008-05-18 01:25:03

    今天看阿里巴巴搜索技术中心文档,无意发现:

    “camera内部是采用java语言来实现的,本接口把camerajava接口封装成C++接口,通过c语言构建jvm来调用camerajava接口

    我就很想一看究竟c++如何调用java程序了。

     

    参考http://www.velocityreviews.com/forums/t145612-jni-call-java-from-c.html

     

     

    D:\lr_scrīpt\MyJNI>java -version

    java version "1.5.0_06"

     

    DemoMain.java内容如:

    import java.io.*;

    public class DemoMain {

    public static void main(String[] args) throws java.io.IOException, java.lang.NullPointerException

    {

    System.out.println("This is a test.");

    }// end main().

    }// end class DemoMain.

     

    采用vc6++ IDE,采用JNI技术实现。

    (1)    编译时:

    C++  preprocessor->additional include directories:

      加入 D:\Sun\AppServer\jdk\include

    2)运行时,

    D:\Sun\AppServer\jdk\jre\bin\server\jvm.dll 加入环境变量。

     

    #ifndef __cplusplus

    #define __cplusplus

    #endif

    #include "jni.h"

    #include <stdio.h>

    #include <stdlib.h>

    #include <windows.h>

    #pragma comment (lib,"D:\\Sun\\AppServer\\jdk\\lib\\jvm.lib")

     

    void main() {

     

    JavaVM *jvm;

    JNIEnv *env;

     

    JavaVMInitArgs vm_args;

    JavaVMOption options[3];

     

    options[0].optionString = "-Djava.compiler=NONE";

    options[1].optionString = "-Djava.classpath=.";

    options[2].optionString = "-verbose:jni";

     

    vm_args.version = JNI_VERSION_1_4;

    vm_args.nOptions = 3;

    vm_args.options = options;

    vm_args.ignoreUnrecognized = JNI_TRUE;

     

    jint res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);

    if (res < 0) {

    fprintf(stderr, "Can't create Java VM\n");

    exit(1);

    };

     

    jclass cls = env->FindClass("DemoMain");

    if (cls == 0) printf("Sorry, I can't find the class");

     

    fprintf(stdout, "This is invokeSimplified4.\n");

     

    jmethodID get_main_id;

     

    if(cls != NULL)

    {

     

    get_main_id =env->GetStaticMethodID(cls,"main","([Ljava/lang/String;)V");

     

    fprintf(stdout, "This is invokeSimplified5.\n");

     

    if(get_main_id != NULL )

    {

          jclass string = env->FindClass("java/lang/String");

          jobjectArray args = env->NewObjectArray(0,string, NULL);

     

          fprintf(stdout, "This is invokeSimplified6.\n");

          int i = env->CallIntMethod(cls, get_main_id, args);

          fprintf(stdout, i+ "This is invokeSimplified7.\n");

    }

    }// end IF.

     

    jvm->DestroyJavaVM();

    fprintf(stdout, "Java VM destory\n");

    }//end main.

     

     

    程序的关键在

    jint res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); 等 jni.h里面的函数

     

  • 一个IP变动引发的血案_by wxc

    2008-05-15 20:17:18

    IT重新划分网段后,机器居然不能正常启动了,检测完内存后,就直接出现please press Enter to active console错误。而且不是一台,而是一大片。这可是要命的问题。想到MAC地址和交换机端口是绑定的,会不会是这个问题呢。转念一想,即使是绑定也不会导致机器不能启动啊。后来咨询了SA,问题得以解决。原来是DHCP惹的祸。启动时,DHCP client和DHCP server端会进行一次握手,client端会得到server给的动态IP和其他一些帧信息,并保存。重新划分网段后,client端的原有信息被篡改,导致握手不成功。于是在启动机器,进行到DHCP的时候就hold住了。知道缘由之后,很简单,拔网线,在PXE disable掉,重起,设IP,插网线,很快的就搞定了。这个血案于是乎就被消灭在萌芽状态了。

  • 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-15 09:58:58

    by jack


        周一汶川强地震,杭州也有明显的震感。下午两点多一阵摇晃后下了楼吃了点冷饮又回到了工作区。看到了报道才知道地震的严重程度。昨天还得到一个让人感到沉重的消息——一个大学的朋友正在绵阳,下落不明了。到我写这篇博文的时候,还是没有他的消息。


        在强大的自然力量面前,生命显得脆弱而又渺小。忽然一下子想起了大学的生活,想起了我的朋友们,特别是这位失踪的地球科学系的同学。昨天从另一位同学那里得知,他是从成都赶过去的,因为当地报了一些动物异常类的所谓“地震前兆”;地震发生前是不是会有前兆目前还不能确定,不过已得到的观察结果似乎还是存在的,但即使震前确有征兆,反过来却不能成立:出现被称作“地震前兆”的现象,并不一定是会发生地震。他出发前还跟他的同班同学说起这些,那同学道“注意安全,带上卫星电话,不怕一万,就怕万一”,他大概觉得没有什么事,就没带;想不到,“万一”却真的出现了。

        想想大学时我们在大气科学上实验课的时候,老师说过,其实自然现象预测是很困难的,无论是天气预报还是地震预测;现在的天气预报在报告晴雨状况时都使用“降水概率”一次,就是这个原因:即使降水概率80%,也有20%可能不会降水。而且,实际上这个数据也不是十分精确,最关键的时候这个数据要在降水发生前12小时内才能获得相对精确的数据。地震是地质活动的剧烈表现形式,岩层的复杂导致对其进行受力分析十分困难:岩层的厚度、结构、岩石种类、运动方式等等,如果真的能把所有状态数据收集起来建立一个超级模型,那么预测它下一步的运动状况还有可能;那也只是可能,因为岩层每时每刻都是运动的,状态数据每时每刻都在变化,能够实时收集吗?

        国外有这种研究的,类似于OO的思想,建立一个模型(类),包含大量的状态数据(属性),各点的运动方式(方法);分析岩层,就把数据套入(实例化),用高速计算机运算下面的状态变化(调用方法,改变属性)。


        想起了一大堆乱七八糟的东西,也不知道自己在想什么了。希望能有好的消息。

        除了为他祈祷,也无法做什么了。还是安心工作吧,无论我们再怎么慌乱,也不能解决问题。

  • 连接池对性能测试施压的影响

    2008-05-14 22:03:56

    by jack

        前几天有人来问我一个关于性能测试施压时的问题:他在设计场景时,并发用户数低于50个的时候场景运行没有什么问题,但设置为60个或更高的时候出现一些502,503错误。


        我去帮他看了一下,在脚本里加入了一些变量的日志输出,看了一下出现错误时访问到什么页面,以及所用的一些参数化数据;排除了数据引起问题(用日志打出来的数据手工操作是没有错误的)。多次调整了并发用户数,重现了他说的问题。


        登录到服务器上看了一下上面的连接量(netstat),发现有150个之多,猜想是不是超过了apache的连接数限制;检查服务器上的httpd.conf,果然找到

    MaxClients       150

    这条设置。马上将参数改为1024,重启apache之后再施压,问题不再出现。

        他又问,为什么60个并发用户却产生了这么多连接。检查他的脚本发现脚本中action确实是单一的访问,按说不该有这么多连接(会有少量的TIME_WAIT);再次登录服务器查看,发现了很多Foreign Address也是本机的连接,相信是应用程序本身访问造成的了。


        这次的问题其实并不复杂,只是并发导致的访问超出了应用服务器设置的限制;但从中可以发现,施压的访问量并不需要达到设置的限制,连接量也有可能达到限制。这跟应用本身也有很大关系。增加压力时逐渐出现错误不要急着下结果;当然根本上的做法应该是事先将连接设置配好。

     

  • 性能测试小小经验

    2008-05-13 09:04:23

        by jiale

        给apathe加载将url某个固定域名替换为变化域名的modul的功能做加载前加载后的对比性能测试,测试的结果发现,加载后的相应时间、tps都比加载前的性能要差,唯独cpu占用率却比加载前要低,但这个modul是非常占cpu的,不可能出现比加载前cpu占用率低的情况,肯定是哪个环节上有问题了。
        原来是由于加载后的tps比加载前的要低,因此cpu占用率也就相应的降低了,为了能够获得可靠的对比数据,我们可以在loadrunner给两个场景设置相同的tps。
        设置方法:打开Controller,选择Goal-Oriented Scenario模式打开Scenario,在Edit Scenario就可以对加载前后两个场景设置相同TPS。
        如此设置后测试,加载后的相应时间、cpu占用率均比加载前的要高,得到了预想的对比测试结果

  • 从一安全测试PPT中摘录的安全测试工具

    2008-05-12 22:58:14

    by liangjz

    HTTPAnalyzer IE实时分析HTTP流
    Companion js—逆向查看java语言编写的网页源码
    Nessus——扫描服务器(协议与端口)
    Paros——扫描工具
    Sss——系统扫描工具
    Sds——数据库扫描工具
    wikto——spider、google hack等
    X-scan

    HTTPAnalyzer 、paros工具都已经下载并实际试验.

    httpAnalyzer能实时扑捉http流,同样包含duration以及header、reponse信息。貌似可以达到ibm pagedetailer、yahoo yslow、firebug的功能。找个时间好好比划。看来前端web页面分析分析的工具也是很多的

     

     

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

    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的编码就需要有一定的质量保证。

  • 注意性能测试脚本运行受业务流程的影响

    2008-05-12 13:55:27

    by jack

        前一阵在做一个日文网站的性能测试。脚本设计好后,发现单个脚本调试有时能通过,有时又通不过(通不过时检查点找不到);通过手工操作是没有问题的。当时由于界面看不懂(日文,汗啊~),所以一时没有搞清楚是怎么回事。曾怀疑该应用性能有问题(代码陈旧,性能确实不好)
        经过翻译后了解到,通不过的情况下出现了一个错误页面,页面上有一行出错的提示,而该提示的内容就是“出错了”(没有具体错误信息,再汗~)。该脚本是一个论坛上回复帖子的操作;忽然灵机一动,猜想会不会是一般的那种论坛防灌水机制——连续操作过快就会抛出错误呢?为验证猜想,做了如下试验:一、在脚本中回复动作前加入60秒的think time,连续循环运行一百次,结果发现没有再出现通不过的情况;二、修改脚本每次运行时重新登录其它帐号,也就是使用不同帐号来回复贴子,结果发现也没有再出现通不过的情况。手工操作只所以不会出问题,是因为手工操作的时间比较长,超过了防灌水限制的时间。
        从上面的试验结果来看,可以想到的解决方案有:1.加入think time或pacing使运行时间间隔大于防灌水限制的时间;2.不断更换帐号登录。两种方式实现不同,产生的压力有异:方案1虽然解决了防灌水限制的时间问题,但由于时间间隔拉长,可能产生的访问压力便达不到要求了,而为了让压力达到要求再增加并发的数量也是不合理的;方案2可以保证产生的压力可以方便的控制,但每次回复动作同时也带上了登录操作,登录操作如果在同一服务器上,则产生了与回复同量的登录请求,可能不符合具体业务的压力要求。还有一个办法就是从代码上修改,将限制去掉或将时间缩短到脚本不会触到的底线。
        可以看出,在性能测试脚本的设计时,一定要考虑好业务本身有没有什么限制使得连续、并发的操作收到影响,因为这些都可能导致脚本运行抛出错误而原因与服务器无关。这类限制一般有单位时间内对操作量的限制、操作超时的机制等。

  • ajax 应用程序

    2008-05-02 13:18:22

      by liangjz

      经过测试在IE6 以及firefox2.0 都可以正常运行的ajax程序。可以用firebug 或者ibm page detailer感觉交互过程

     

    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head>
    <title>ajax demo</title>

    <scrīpt type="text/javascrīpt" language="javascrīpt">
     var httpRequest =false;
        function makeRequest(url) {       
     document.getElementById("result").innerHTML="";
            if (window.XMLHttpRequest) { // Mozilla, Safari, ...
                httpRequest = new XMLHttpRequest();
                if (httpRequest.overrideMimeType) {
                    httpRequest.overrideMimeType('text/xml');
                    // See note below about this line
                }
            }
            else if (window.ActiveXObject) { // IE
      var versions = ['Microsoft.XMLHTTP', 'MSXML2.XMLHTTP','MSXML.XMLHTTP', 'Msxml2.XMLHTTP.7.0',

    'Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.5.0', 'Msxml2.XMLHTTP.4.0', 'MSXML2.XMLHTTP.3.0', ];
       for(var i=0; i<versions.length; i++) {
            try {
           httpRequest = new ActiveXObject(versions[i]);     
        } catch(e) {}
       }
             }
            if (!httpRequest) {
                alert('Cannot create an XMLHTTP instance');
                return false;
            }
     
            httpRequest.onreadystatechange = alertContents;   //function() { alertContents(httpRequest); };
            httpRequest.open('GET', url, true);
     httpRequest.setRequestHeader('Cache-Control','no-cache');
            httpRequest.send('');
        }

        function alertContents(){
            if (httpRequest.readyState == 4) {
                if (httpRequest.status == 200) {
                    //alert(httpRequest.responseText);
      document.getElementById("result").innerHTML =httpRequest.responseText;
                } else {
                   document.getElementById("result").innerHTML='There was a problem with the request.';
                }
            }
        }
    </scrīpt>
    </head>
    <body>
    <span
        style="cursor: pointer; text-decoration: underline"
        ōnclick="makeRequest('http://127.0.0.1:3000/my_test/')">
            Make a request
    </span>
    <br>
    <span id="result" style="" >
    </body>
    </html>

     

    在本地启动mongrel服务器侦听3000端口

1566/8<12345678>
Open Toolbar