• 前端自动化测试工具doh学习总结(二)


      一、robot简介

      robot是dojo框架中用来进行前端自动化测试的工具,doh主要目的在于单元测试,而robot可以用来模仿用户操作来测试UI。总所周知,Selenium也是一款比较流行的前端自动化测试工具,与Selenium相比robot的优点在于robot触发的浏览器事件是真正的用户操作事件,而Selenium中的事件属于“合成事件”。打个比方,用户在一个textbox元素上触发了mousedown事件,但是在触发mousedown事件之前,肯定会触发mouseover等事件。如果使用Selenium的工具的“合成事件”则不会触发mouseover等事件。对于使用dojo框架的开发者来说,robot更有利于同dijit交互,包括自定义dijit;当然最重要的一点是,在研究selenium的过程中,我发现Selenium有严重的浏览器兼容性问题!于是果断放弃。

      言归正传,下面来具体介绍robot。robot主要有三部分组成:

      doh/robot,这里面包含的是robot的核心内容,与dojo框架没有耦合关系,可以单独拿出去作为一个自动化测试工具。

    1 require(["doh/robot"], function(doh){
    2     ...
    3 });

      dojo/robot,该模块使用dojo核心技术,支持doh/robot的全部方法,主要在doh/robot的基础上增加了两个方法:mouseMoveAt和scrollIntoView。

    1 require(["dojo/robot"], function(doh){
    2     ...
    3 });

      dojox/robot,该模块在dojo/robot的基础上增加了两个重要方法:initRobot和waitForPageLoad。为什么说他们重要,因为他们可以再一个测试案例文件中控制其他的测试文件,后文将详细。

    1 require(["dojox/robot"], function(doh){
    2     ...
    3 });

      

      二、基本步骤

      doh/robot测试主要分为以下四步:

      1、实例化一个doh.Deferred对象

      2、执行交互命令

      3、设置一个超时函数来验证交互结果

      4、在runTest中返回deferred对象

      这个事例中页面上一个文本输入框,包含hi两个字符,然后模仿用户输入“ again”动作。示例,robot实际上是一个applet第一次访问的话会提示安装。

     1 require(["doh/runner", "doh/robot"], function(doh, robot){
     2     doh.register("doh/robot",
     3     {
     4         name: "dojorobot1",
     5         timeout: 6900,
     6         setUp: function(){
     7             document.getElementById('textbox').value="hi";
     8         },
     9         runTest: function(){
    10             var d = new doh.Deferred();
    11             robot.mouseMove(30, 30, 500);
    12             robot.mouseClick({left:true}, 500);
    13             robot.typeKeys(" again", 500, 2500);
    14             robot.sequence(d.getTestCallback(function(){
    15                 doh.is("hi again", document.getElementById('textbox').value);
    16             }), 900);
    17             return d;
    18         }
    19     });
    20     doh.run();
    21 });
        <form>
        <input type="text" value="hi" id="textbox" style="position:absolute; left:0px; top:20px; font-family:system;"></input>
        </form>

      这里要说以下robot.sequence和d.getTestCallback这两个函数,sequence是robot中的一种同步方式书写的setTimeout,下文delay中会讲到这类方法的优势。doh.Deferred是doh中对dojo/Deferred的扩展,加入了许多方法,getTestCallback就是其中之一,该函数作用就在于交互命令执行完成后验证交互结果,并将结果告知测试框架从而决定显示绿条还是红条,通常一个测试案例中只有一处调用该方法。一下是该函数的源码:

     1 getTestCallback: function(cb, scope){
     2         var _this = this;
     3         return function(){// 该函数返回一个闭包给sequence函数,从而在一定时间后调用该闭包
     4             try{// 执行验证函数,监控结果,并将结果通知测试框架
     5                 cb.apply(scope||doh.global||_this, arguments);
     6             }catch(e){
     7                 _this.reject(e);// 红条
     8                 return;
     9             }
    10             _this.resolve(true);// 绿条
    11         };
    12     },

      

      三、API介绍

      1、基本参数

      下面以doh/Robot中typeKeys为例介绍一下robot API的基本参数,具体提供了哪些API可以在这里查看:API

     1 typeKeys: function(/*String||Number*/ chars, /*Integer, optional*/ delay, /*Integer, optional*/ duration){
     2       // summary:
     3       //            Types a string of characters in order, or types a dojo.keys.* constant.
     4       //
     5       // description:
     6       //           Types a string of characters in order, or types a dojo.keys.* constant.
     7       //           Example: robot.typeKeys("dijit.ed", 500);
     8       //
     9       // chars:
    10       //            String of characters to type, or a dojo.keys.* constant
    11       //
    12       // delay:
    13       //            Delay, in milliseconds, to wait before firing.
    14       //            The delay is a delta with respect to the previous automation call.
    15       //            For example, the following code ends after 600ms:
    16       //                  robot.mouseClick({left:true}, 100) // first call; wait 100ms
    17       //                  robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all
    18       //
    19       // duration:
    20       //            Time, in milliseconds, to spend pressing all of the keys.
    21       //
    22   }

      delay,顾名思义目的在于延迟,在webAPP中经常会有动画效果,等待动画结束后在执行click等事件,所以每一个API中与UI交互的函数都会提供这个方法。该方法内部使用setTimeout方法实现,既然这样,为什么不让用户直接使用setTimeout方法?原因在于dojo可以为我们自动调整一系列动作的执行顺序及间隔。举个例子,如果使用setTimeout,动作f1需要在300ms后执行,动作f2需要在f1之后100ms执行那就需要设置f2的timeout时间为400ms,这时候f1之前要加一个动作f3在f1之前200ms执行,

    现在我们就需要挨个该f1跟f2的时间参数,如果f1之后有100个动作要执行。。。。God bless you。使用robot API提供的函数,我们只需要设置每个动作之间的时间间隔即可。

    1     setTimeout(f1, 300);
    2     setTimeout(f2, 400);
    3     =>>
    4     setTimeout(f3, 300);
    5     setTimeout(f1, 500);
    6     setTimeout(f2, 600);    
    1     var mask = query("div.ovwHighlight")[0];
    2     robot.mouseMoveAt(mask, 200, 200);
    3     robot.mousePress({left: true}, 100);
    4     robot.mouseMoveAt(mask, 200, 200, 10, 10);
    5     robot.mouseRelease({left: true}, 100);        

      duration便是的是执行这个动作所需要的时间,以typeKeys为例,假设duration设置为1800ms,char为“abc”,则输入a、b、c这三个字符各占600ms。

      2、mouseMoveAt

      dojo/Robot中提供了一个mouseMoveAt函数,在上文的示例中,我们可以看到鼠标移动函数用的是mouseMove函数,该函数与mouseMoveto函数都是通过相对于当前浏览器document的x、y坐标来定位元素,而mouseMoveAt则是通过选择符或者node节点来定位元素。

     1 mouseMoveAt : function(/*String||DOMNode||Function*/ node, /*Integer, optional*/ delay, /*Integer, optional*/ duration, /*Number, optional*/ offsetX, /*Number, optional*/ offsetY){
     2     // summary:
     3     //            Moves the mouse over the specified node at the specified relative x,y offset.
     4     //
     5     // description:
     6     //           Moves the mouse over the specified node at the specified relative x,y offset.
     7     //           You should manually scroll off-screen nodes into view; use dijit.robot for automatic scrolling support.
     8     //           If you do not specify an offset, mouseMove will default to move to the middle of the node.
     9     //           Example: to move the mouse over a ComboBox's down arrow node, call doh.mouseMoveAt(dijit.byId('setvaluetest').downArrowNode);
    10     //
    11     // node:
    12     //            The id of the node, or the node itself, to move the mouse to.
    13     //            If you pass an id or a function that returns a node, the node will not be evaluated until the movement executes.
    14     //            This is useful if you need to move the mouse to an node that is not yet present.
    15     //
    16     // delay:
    17     //            Delay, in milliseconds, to wait before firing.
    18     //            The delay is a delta with respect to the previous automation call.
    19     //            For example, the following code ends after 600ms:
    20     //                  doh.mouseClick({left:true}, 100) // first call; wait 100ms
    21     //                  doh.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all
    22     //
    23     // duration:
    24     //            Approximate time Robot will spend moving the mouse.
    25     //            The default is 100ms.
    26     //
    27     // offsetX:
    28     //            x offset relative to the node, in pixels, to move the mouse. The default is half the node's width.
    29     //
    30     // offsetY:
    31     //            y offset relative to the node, in pixels, to move the mouse. The default is half the node's height.
    32     //

      所以上文中的实例可以这样写:

     1 require(["doh/runner", "dojo/robot"], function(doh, robot){
     2     doh.register("doh/robot",
     3     {
     4         name: "dojorobot1",
     5         timeout: 6900,
     6         setUp: function(){
     7             document.getElementById('textbox').value="hi";
     8         },
     9         runTest: function(){
    10             var d = new doh.Deferred();
    11             robot.mouseMoveAt(document.getElementById('textbox'), 500);
    12             robot.mouseClick({left:true}, 500);
    13             robot.typeKeys(" again", 500, 2500);
    14             robot.sequence(d.getTestCallback(function(){
    15                 doh.is("hi again", document.getElementById('textbox').value);
    16             }), 900);
    17             return d;
    18         }
    19     });
    20     doh.run();
    21 });

      3、initRobot

      对于WebAPP来说,都需要将测试代码与主程序代码分离。但测试文件执行时需要与主程序中的UI交互,这时轮到initRobot闪亮登场了。该函数位于dojox/Robot中,所以我们需要引入dojox/Robot。initRobot主要的作用就在于将当前测试文件的执行环境转变为主程序环境,这时robot.doc、robot.window代表的则是主程序中的document和window。比如说你想使用document.getElementById(‘login')来获取主程序中的login元素,那么你需要这样来写robot.doc.getElementById('login'),官方文档中提到使用dojo模块中的方法的执行环境都是主程序环境,遗憾的是我在实际使用中发现并非如此,有的模块方法的确是主程序环境比如query,而有的则并非主程序环境比如registry。

      具体示例可以访问这里,查看源码你会发现这里面并没有robot的测试代码,该文件位于dojo-release-1.9.1-srcdijit ests目录下,robot测试代码位于dojo-release-1.9.1-srcdijit ests obot目录下。

    参考文档:https://dojotoolkit.org/reference-guide/1.9/util/dohrobot.html#doh-robot

    下一篇我们将具体看一个完整robot示例,以及将robot与selenium联合使用进而将他与CI工具集成。同时我们还会讨论robot异步函数的美中不足。

  • 相关阅读:
    汽车文化【1196】
    西方经济学
    计算机组成原理【0013】
    C语言程序设计【0039】
    教育学【0405】
    管理学[9080]
    专业英语【0089】
    计算机基础1056
    letcode每日一题-上升下降字符串
    Flowable学习-flowable
  • 原文地址:https://www.cnblogs.com/dojo-lzz/p/3734713.html
Copyright © 2020-2023  润新知