结合个人工作,交流软件测试领域相关的概念、测试工具、测试自动化等相关话题,欢迎各路朋友多多交流~

发布新日志

  • 使用开源库,自己来实现基于HTTP的交互模型

    2011-11-09 17:30:56

       一直这样认为,测试人员不是简单地掌握业务、懂得一些测试方法或会使用一些测试工具就可以了。对一个好的测试人员来说,知识面很重要,专业技能也很必要,包括至少会自己动手创造工具。
       这个贴源于前两年为解决一个交互比较复杂的系统的自动化问题,而不用selenium、watir等一些工具,进行自己”钻木取火“而采用一个Spnk库来简单简单实现了HTTP交互,一是解决问题,二也是练习之作。当然,您也可以继续深入做啦。但对我来说,已经够了。先简单介绍一下封装使用过程吧,后续再抽空分享一下在我的自动化解决方案中的应用。
       Spnk一个开源库,它已经封装http协议的处理细节,提供了常用的SP_NKHttpRequestSP_NKTcpSocketSP_NKHttpProtocolSP_NKHttpResponse四个类,提供了完整的实现HTTP操作的一些方法。下面结合一个简单的案例(案例会做一些简化)说明一下封装过程:      

    一、实现一个最基础的请求请求、响应交互:

    创建SP_NKHttpRequest对象,补充HTTP基本消息头信息

    SP_NKHttpRequest request;

    request.setMethod(“POST”);   //这个填写PostGet影响不大,真正调用是后面的SP_NKHttpProtocol的方法决定

    request.setVersion( "HTTP/1.1" );

    request.addHeader( "Connection", "Keep-Alive" );

    request.addHeader( "Host", host.c_str() );// 域名 如www.qq.com

    request.setURI( content.c_str() ); 

    上面的content变量,填充的是完整的http相对路径和请求参数,如:

    /cgi-bin/direct.cgi?AcctType=1&CallBackURL=app_callback.cgi&CmdCode=pay&Extend=&PayAmt=&PayChannel=vnet&ServiceCode=LTMCLUB&SessionKey=&SessionType=NoSession&UserIP=&VerifyKey=


    然后需要和web服务器(apache)建立连接,初始化SP_NKTcpSocket的对象

    SP_NKTcpSocket socket(“172.27.196.213”, 80);

     

    OK,建立连接后,直接使用SP_NKHttpProtocolget(当然也有post)方法,即可完成发送请求操作:

    int iRet = SP_NKHttpProtocol::get( &socket, &request, &response );  

     

    其中引用的response即是 SP_NKHttpResponse对象啦。对响应结果,可以判断如果get返回!=0 即说明http请求失败.

    SP_NKHttpResponse对象提供几个方法常用方法:getStatusCode()获取HTTP状态码;getContent()获取响应内容;getHeaderCount()返回消息头条目;getHeaderValue( int index )getHeaderValue( const char * name )获取头内容。

    如:

           if(response.getContent()!=NULL)

           {

                  sResponse = (char *)response.getContent(); //取出响应内容

           }

          

           strCookie = response.getHeaderName(“Set-Cookie”); //取出响应cookie内容

     

    至此,一个简易的HTTP处理模型就实现了。

     

    二、解决客户端cookie的问题

    涉及的HTTP的测试,cookie是必不可少的,如在我的实现案例中,cookie中保留了必要的加密交易信息。如何解决cookie的问题,使用spnk库可以很轻易地解决:

    strCookie = response.getHeaderName(“Set-Cookie”); //取出响应cookie内容

    http响应中,填写客户端cookie是放在响应的Set-Cookie消息头中,直接调用getHeaderName方法即可取得; 

    http响应也有可能有多个Set-Cookie的响应,这个时候也可以用下面的方式去遍历,分别取出来保存:

    //遍历包头

           for(;(pszHenadName = (char *)response.getHeaderName(i))!= 0;i++)

           {

                  //cookie需要抛出去给第二、三步交易使用

                  if(strcmp(pszHenadName,"Set-Cookie") == 0)   

                  {

                                string cookie = response.getHeaderValue(i);

                                tmpCookies+= cookie.substr(0,cookie.size()-7);  //简单起见,将多个cookie直接拼接在一起了。但由于每个cookie后面都有带一些结束符合,因此在合并时要去除7个字符(cookie对这个格式有严格要求)

                  }                         

           }

     

    当下一步交易需要使用到cookie时,直接将上面获取到的cookie放入到HTTP请求消息头中即可生效:

    request.addHeader( "Cookie", cookie.c_str() );

     

    三、如何解决客户端重定向的问题

    重定向有服务器端的和客户端的,对服务器端就不用我们担心了,apache或其他web服务器会搞定了。但对客户端的重定向,在自己封装web处理时,需要手动处理。一个http的客户端重定向过程如:(以Servlet的重定向为参考)

    在客户端第一次请求后,服务器端返回一个响应,告诉浏览器(客户端)需要跳转到第二个页面去处理,浏览器做响应处理后,即再向服务器端发起对第二个页面的请求。此处的跳转主要两个方式:

    1、  在响应消息头的Location头中:

    判断重定向,应该首先判断http应答中的状态码,3xx的是重定向,Location内指定重定向的地址.

    如果需要进行重定向的,在第一次响应中Location头信息类似:http://www.qq.com/cgi-bin/xxxxxx.cgi?SPItemResponse=35000003%24

    因此使用下面方法去取得即可:

    String nextJumpUrl = response. getHeaderValue(“Location”)

    if(nextJumpUrl!=NULL)

    {

           。。。 //这个时候就可以参照第一部分的方式,再创建一个http请求进行处理啦;

    }

     

    2、  在响应内容中使用javascript脚本进行跳转:

    使用javascript进行调整的方式比较简单,生成的javascript内容就记录在response.getContent()中,如下:

    <script. language=javascript>

    window.location.href='http://www.qq.com/xx/xxx.jsp?SPItemRequest=35000003%24pyXLXj57lzr%2FFYBN%2Bik3URnblwgf6l6AxeCk%2F9Pzt5edWDuveb4bT9k%2B8bYdfl05DYIwTT2qi0sebZuGR74MY7H1hRR4a%2FyKvO1Xv4MIMQx5qgaEO6eYxYRIYBDmSUb%2FGx70E2OASAND7ssBd6lc7WbsEYBpQiKVPzFBI5MFNDc6iTs0lQFMfAfhuLlBwVmaR7G8wBSbTFkcJ%2F4gDYKEyQZKlicEIUETe8bq4cnf7TWK8ik6ADoqb26Bm7ORKQWABx0lIX23Iw1lNp9pGN%2Buz5u8%2FFAaIepg%2BvzN1Gm';

    </script>

    因此,可以使用正则表达式去匹配window.location.href=’*’,解释出其中的内容,然后同样参照第一部分的方式,创建一个http请求去处理即可。


    至此,一个简单的HTTP交互模型就实现了。其实还是很简单的,测试人员都应该可以做起来。至于如何和实际应用结合,期待下次的文章吧。

  • 使用.net开发web自动化测试工具

    2009-06-29 11:17:37

    前段时间,由于测试需要,使用C#结合WatiN组件,开发了一个小小的web自动化测试工具。

    WatiN 是一个非常简单灵活的测试框架,可以模拟用户在客户端浏览器中的大部份操作,API也比较简单。

    一、实现url调用和web控件的控制很简单,参考下面代码:

      for (int i = 0; i < txtUrls.Length; i++)
                {
                    try
                    {
                        WatiN.Core.DialogHandlers.AlertDialogHandler dh = new WatiN.Core.DialogHandlers.AlertDialogHandler();

                        using (IE ie = new IE(txtUrls[i]))    //调用URL,txtUrls[i]为Url地址
                        {
                            ie.AddDialogHandler(dh);//增加一个控制句柄

                            ie.CheckBox("cblSets_6").Checked = false;
                            ie.CheckBox("cblSets_8").Checked = true;                        
                            ie.Button("btnSave").ClickNoWait();   //执行保存
                            ie.RemoveDialogHandler(dh);
                            ie.Close();
                        }
                    }
                    catch (Exception e)
                    {
                        sb.Append("Execute Err:").Append(txtUrls[i]).Append(";");
                    }               
                }

    二、WatiN 里常用的类

    WatiN.Core.Find

    最普遍使用的类就是Find类的,它是一个工厂类,主要使用它的静态方法来实现一些查询条件。比如像上面的例子中的这个语句“ie.Button(Find.ByName("btnG")).Click();”就调用了Find的静态方法ByName来查询一个name属性为指定值的HTML元素,然后再调用IE对象的Button方法把这个元素转换为按钮对象。

    WatiN.Core.IE

    这应该是最关键的类了。他常用的方法是和属性为

    属性

    HtmlDialogs 返回当前对象用JavaScript打开的模式窗口(需然帮助文档中说非模式窗口也包括在内,但在试用中发现用window.open打开的窗口没有被)

    Frames 返回当前的象里的所有Frames

    方法

    静态方法 AttachToIE 与一个已经找开的IE关连。

    Button,TextField,Image,Div 等一系列方法。与Find对象共同使用用于返回IE中特定的按钮,输入框,等HTML元素。

    三、应用中的一些技巧

    1、如何去捕获一个新弹出的窗口。

    背景:有些链接是从一个新弹出的窗口中打开的,我如果关连上这类型的窗口。

            public void Login(string uid, string passwd)
            {
                string url = Host + "frmlogon.aspx";
                ie = new IE();
                ie.GoTo(url);
                ie.WaitForComplete();
                ie.TextField(Find.ById("txtUserName")).Value = uid;
                ie.TextField(Find.ById("txtPwd")).Value = passwd;
                ie.Button(Find.ById("btnLogin")).ClickNoWait();//这个方法改成这样,那点击后就不会等代码IE完成了。
                //下面这句就是处理登陆的技巧所在,系统打开了另一个窗口。
                //下面这句就是用正则表达式捕获这个窗口。
                ie = IE.AttachToIE(Find.ByTitle(new WatiN.Core.Comparers.RegexComparer(new System.Text.RegularExpressions.Regex(".*自动化测试.*"))));
                ie.WaitForComplete();
                MainFrame. = ie.Frame(Find.ByName("MainFrame_00001"));
                MenuFrame. = ie.Frame(Find.ById("leftMenu"));//把系统里的Frame先保存下来。
            }

    2、对于alert 、confirm 等javascript弹出的窗口的捕获。

    背景:一个系统经常会使用以上这些javascript来弹出一些提示信息,如果捕获这些窗口,并模拟用户点击这些窗口上的OK或Cancel按钮?

    方案:其实WatiN在默认情况下,都会自动地去点击这些弹出式窗口上的Cancel按钮的,但如果用户要明确点击哪些事件的话可以对IE对象增加一个“查看器”()

    protected void f()
            {
                WatiN.Core.DialogHandlers.ConfirmDialogHandler dh = new WatiN.Core.DialogHandlers.ConfirmDialogHandler();

                ie.AddDialogHandler(dh);
                doc.Button(Find.ByName("yzp_dic_btn_cls")).ClickNoWait();//把原来的Click改成这个方法。
                dh.WaitUntilExists(3);//等待弹出窗口的出来。最多等三秒。
                dh.OKButton.Click();//点击这个窗口的OK按钮
                ie.RemoveDialogHandler(dh);
                doc.Button(Find.ByName("btn_close")).Click();
            }

    3、关于用JS弹出的Modal窗口(模式窗口)的处理.

    背景:有些地方需要弹出模式窗口来处理数据。

    方解:当点击了弹出模式窗口的按钮或连接后马上用IE对像的HtmlDialogs属生来获取模式窗口。

             protected void f()
            {
                //假设下面这行代码会弹出一个模式窗口把原来的Click改成这个方法。
                //记住这里要用ClickNoWait而不能用Click,否则在模式窗口关闭之前代码不会继续执行。
                ie.Button(Find.ByName("yzp_dic_btn_cls")).ClickNoWait();
                ie.HtmlDialogs[ie.HtmlDialogs.Length - 1].TextField(Find.ByName("Q")).Value = "Hello";
                ie.HtmlDialogs[ie.HtmlDialogs.Length - 1].Button(Find.ByName("btn_query")).Click();

    WatiN 官方网址:http://watin.sourceforge.net/ 可以在上面下载组件。

  • 主动地进行测试

    2008-05-06 00:30:02

        测试在软件开发流程中似乎处于下游,在执行和操作上看以处于被动地位。于是乎,很多时候,测试人员就会产生如下一些疑问(摘自某位网友的疑问,其实也是很多普通测试人员共有的疑问):
        1、测试不知道自己在测试什么
        2、测试人员的依据不对
        3、测试人员是完全黑盒的,没有关注开发怎么实现
        4、测试人员没有充分测试
        5、测试人员完全被动

        结合我个人的一些实践,谈谈以上问题。
        首页,对1、2、3个问题,其实归结起来还是对需求分析的不透彻导致的。因为对需求分析不足,不知道当前版本开发人员做了哪些修改,所以也就无法在测试设计时,设计出很好的针对性用例来;同样因为对需求分析的不足,对客户不了解,对客户需要什么不了解,也就自然没有了测试依据! 在软件能力成熟度模型(CMM)中,特别强调评审的工作,其实,测试人员需要参与到需求、设计的评审去中,这也是一个需求分析的过程。 当然,不否认现在很多企业,根本就还不存在这种过程规范,那么这就需要我们测试人员去推动它。推动开发提供需求、设计文档,推动参与到需求、设计文档的评审中去。
        在一些比较标准流程的软件企业中,测试其实也有专门的测试需求分析这一阶段的,这些工作由专门的TSE(相对于开发的SE来说,也都是一些技术牛人)来进行,充分说明了对这一块的重视。

        另外,对第4点,更多的应该是对系统、对业务使用的熟悉不够导致的。在居于测试已经确认的开发的修改范围后,测试用例的针对性和覆盖度,那么就在于你对系统结构的熟悉了,以及你对系统的业务应用场景的熟悉。就像经典的三角形的测试案例一样,因为你对三角形、等边、等腰的概念熟悉,那么很容易就设计了针对的用例,结合数字方面常见的错误,也就可以做出很好的覆盖来。

        对最后一点,我并不认同。主动与否,不是你的岗位决定,而是你个人决定的。如果企业在过程管理比较规范的话,那么规范应该不会让你被动的;如果没有规范的话,测试人员也可以主动起来。就像需求、设计文档,测试人员可以主动参与;另外,在版本参与上,也需要提前介入,而不应该等到版本转测试以后才匆忙介入, 这样就会导致在需求分析、开发实现特性、测试计划、测试设计等方面都被动。 测试人员也需要对项目全局掌控,特别是在需求变更上;再有一点,测试人员应该是站在用户角度上的(需求一端的),是帮用户验证软件的。
        其实,对于一些流程操作不规范的企业,测试人员更应该本着软件质量保证的目的出发,去主动地推动相关过程的规范化。流程的规范不是一朝一夕的,需要不停地去总结和改进。

     

  • 当开发做了需求之外的事情时...

    2008-03-08 12:31:11

       在软件测试阶段,可能我们对软件的需求变更的事情已经很熟悉了,这个的影响,更多的影响到你的测试计划,对软件缺陷的漏测影响还不算很大。但当开发人员,在需求的背后,悄悄地“捅”你一刀--根据他的想法,修改了计划外的东西!那这就很要命了,测试对需求不可控!很容易就造成了漏测。这在一些走敏捷开发、敏捷测试流程的企业里,算是司空见惯的事情。那么,面对这种现状下,我们该怎么去控制,怎么使漏测的可能性降低呢?

        首页,这个当然得从软件开发流程上去把握了。在流程上,我们可以操作的,一方面是将这个问题暴露给整个流程上的相关人员(可以通过群组邮件或在质量会议上),陈述这种“暗箱”操作的危险性,让PM强调开发流程,同时让开发人员增强这种缺陷的意识。另一方面,我们可以提高版本转测试的门槛。在转测试时,对开发的交付件进行严格审查,特别是对单元测试报告,对版本变更汇总,还可以更强硬地要求开发人员提供修改说明,描述具体修改了哪个模块、修改了哪一行的代码,这样,也可以在一定程度上,让测试人员对版本的修改更有把握,测试的针对性方面也会做得更有效。

        除了在流程上的掌控外,我们还需要从测试技术上来实现真正的自控。为何说是"真正的自控”呢? 开发流程的东西,要所有人员很正直、很严格地遵守才行,如果开发转测试的交付件里,也很“无私地”不体现他的"额外工作"时,那也只能在我们测试这边来把握了。所以测试要建立在完全不信任开发的情况下(好像这个也应该是测试的本质)。对转测试版本,我们首先可以使用代码比较工具(如beyond compare),对前后版本进行比较,然后我们再根据需求来分析一下代码的修改,这样,我们对修改的合理性,对意外的修改也可以一目了然了,这样在做测试时,更可以有针对性地执行。笔者,曾经就在一个版本中发现开发人员无端去掉了一个加锁的操作(他以为这个线程锁,而我们这个进程现在只支持单线程),然后笔者使用多进程方式,发送交叉请求,问题就出现了。这个就是比较分析代码的功劳(因为这次需求根本就不涉及到多进程,与压力有关)。 还有一种工作就是进行压力的测试,笔者一贯都比较提倡,不管这个版本需求有无涉及到压力的影响。通过压力,我们可以测试一下交叉的请求,也可以关注一下内存泄漏方面的问题。这个笔者也有多次经历。它对于发现并发冲突、内存泄漏方面、性能下降等问题都很有帮助。再一个工作,如果可能的话,可以使用一些内存泄漏工具对软件进行内存泄漏方面的分析(如valgrind,purify等)。在一个版本修改少时,很多开发人员都会忽略对内存泄漏的检查。

        当然,以上这些方法需要具备一定的条件。如需要有质量管理这概念、测试可以接触源代码等。

        以上内容,纯属笔者亲身体验,有感而发。望各方朋友,多加指点。

Open Toolbar