zoukankan      html  css  js  c++  java
  • 【E2E Testing】学习(4):Protractor了解

    时间紧迫,先罗列一些内容

    • Protractor的安装与使用
    • Protractor的语法解释
    • 术语以及原理
    • 一些普遍的JS调试语法

    安装与使用


    API接口

    • by与locator
    • element与elementFinder/
    • promise
    • element.all
    • browser

    基于Protractor编写的测试角度,核心就是找到DOM元素,并与其交互,然后验证其交互结果是否与预期一致(说白了,就是写一个模拟器,模拟用户在网页上的操作动作)。所以查找DOM元素(这还涉及网页的功能逻辑)并与之交互(网页的交互逻辑)非常重要。

    by与locator

    首先是在DOM树中找到目标的DOM元素。可以才用by函数,它提供了多种查找DOM元素的方式。以下面这行代码为例

    by.id('gobutton')
    

    by函数用查找id的方式确定一个locator对象(也就是说,这个locator对象,应该是网页DOM树中、id为‘gobutton’的元素)。

    element与elementFinder

    接下来需要有函数去接收这个locator对象,才能对DOM元素进行交互操作。以下面这行代码为例

    element(by.id('gobutton'))
    

    Protractor提供了一个全局的对象(类或者什么都可以,现在看来都差不多)element,它可以去接收上述的locator对象,并返回一个elementFinder对象。需要说明的是,这个时候对于elementFinder对象无法操作。只有对它发出相关的指令后,才有交互。举个例子:

    element(by.id('gobutton')).click();
    

    该代码在获得elementFinder对象之后,要寻求与其进行交互,交互动作为click事件。除了click方法外,elementFinder对象有很多方法,如

    //--view code--
    <ul class="items">
      <li>First</li>
      <li>Second</li>
      <li>Third</li>
    </ul>
    
    //--testing code--
    element.all(by.css('.items li')).then(function(arr) {
      expect(arr.length).toEqual(3);
    });
    View Code

    promise

    所有elementFinder对象的方法,返回值都是一个promise对象,举个例子:

    getTitleText() {
        return element(by.css('.navbar-brand')).getText() as Promise<string>;
    }
    

    这个函数(方法)封装了elementFinder的getText方法,并返回了promise对象(字符型)。所以当想要验证交互结果的时候,需要对promise对象再进行操作。

    //--view code--
    <ul class="items">
      <li>First</li>
      <li>Second</li>
      <li>Third</li>
    </ul>
    
    //--testing code--
    element.all(by.css('.items li')).then(function(arr) {
      expect(arr.length).toEqual(3);
    });
    View Code

     测试代码先获取样式为item li的所有元素(array对象),然后获取这个对象,再去计算对象包含的元素数量,并判断是否等于3。该代码传递出两个信息:

    1. 可以使用then方法输出值。假设输出的对象(数组、文本、属性值都可以)为object,实际上形式应该是.then(function(object)){...}。花括号内可以对传入的参数(对象object)进行操作。
    2. 直接使用expect方法来操作值。示例代码其实是先输出值,再操作值。但是完全可以将两步合并成为一步。将arr这个数组对象,用element.all(by.css('.items li')).then(function(arr)代替

    element.all

    顺便说说element.all。尽管是element对象的一种方法,但是返回值是elementFinder array,即elementFinder的数组集合。所以,其实这种方法就是另一类elementFinder对象了。它的方法首先是针对array对象的,然后若提取出其中的元素,也可以调用elementFinder的方法。举些例子:

    //--view code--
    <ul class="items">
      <li class="one">First</li>
      <li class="two">Second</li>
      <li class="three">Third</li>
    </ul>
    
    //--testing code--
    //1.filter, actually it is a iteration
    element.all(by.css('.items li')).filter(function(elem, index) {
      return elem.getText().then(function(text) {
        return text === 'Third';
      });
    }).first().click();
    //this paragraph of code would act on each element in the array and find/filter the ones whose text is 'Third'.
    //btw, return the first element and click it
    
    //2.get-->Get an element within the ElementArrayFinder by index.
    expect(element.all(by.css('.items li')).get(0).getText()).toBe('First');
    
    //3.first-->Get the first matching element for the ElementArrayFinder.
    //4.last-->Get the last matching element for the ElementArrayFinder.
    
    //5.count-->Count the number of elements represented by the ElementArrayFinder.
    expect(element.all(by.css('.items li')).count()).toBe(3);
    
    //6.each-->Calls the input function on each ElementFinder represented by the ElementArrayFinder.
    element.all(by.css('.items li')).each(function(element, index) {
      // Will print 0 First, 1 Second, 2 Third.
      element.getText().then(function (text) {
        console.log(index, text);
      });
    });
    //this function is equal to iteration
    
    //7.map-->Apply a map function to each element within the ElementArrayFinder.
    let items = element.all(by.css('.items li')).map(function(elm, index) {
      return {
        index: index,
        text: elm.getText(),
        class: elm.getAttribute('class')
      };
    });
    expect(items).toEqual([
      {index: 0, text: 'First', class: 'one'},
      {index: 1, text: 'Second', class: 'two'},
      {index: 2, text: 'Third', class: 'three'}
    ]);
    //this function reminder me of HashMap?
    
    //8.reduce-->Apply a reduce function against an accumulator and every element found using the locator (from left-to-right).
    let value = element.all(by.css('.items li')).reduce(function(acc, elem) {
      return elem.getText().then(function(text) {
        return acc + text + ' ';
      });
    }, '');
    expect(value).toEqual('First Second Third ');
    //this is like string joint?
    
    more information, see the official document:http://www.protractortest.org/#/api?view=ElementArrayFinder
    View Code

    特别说明的是,对于elementFinder对象(无论是一个对象还是数组对象),都可以用$$(elementFinder array object)/$(elementFinder object)缩写简化(主要是有时候看到这样的缩写,要是不知道的话,就懵了)

    element.all(by.css('.items li')).then(function(arr) {
      expect(arr.length).toEqual(3);
    });
    
    // Or using the shortcut $$() notation instead of element.all(by.css()):
    $$('.items li').then(function(arr) {
      expect(arr.length).toEqual(3);
    });
    

     还有对浏览器的操作browser


    术语以及原理

    • 异步
    • 链式调用

    这两个术语是Protractor框架代码运行以及编写的特点。我不太懂,所以也写一写。 

    异步

    说个不成熟的理解。异步特别像多任务同时进行,具体说,你用element对象的不同方法,又是按照id找,又是按照样式找,又是点击,又是键入数值,这些都可以一起完成。谁先谁后,完全取决于代码执行的速度。那么同步呢?就是一个任务完成后再是另一个任务。(特别有意思的是,我研究生学优化的时候,同步的概念和计算机这里的异步是一个意思)。知乎上,我选了一个很有趣的例子,可以说明这个问题。

    链式调用

    Protractor对象的采用链式调用。一开始觉得这个名词很高深,仔细了解过恍然大悟,其实早有接触过。先说非链式调用,以C#.NET为例,比如

     double val = zero2null2(double.Parse(tb_val.Rows[i][0].ToString()));
    

    ToString是第一个函数,double.Parse是第二个函数,zero2null2是第三个函数。这样阅读代码,是从里往外读,带来一定的不便利,而且书写代码也很麻烦。如果没有事先想好,那么多半是写了里面,而是剪切掉,写外面的函数在粘进来。

    那么链式调用呢?还是之前的代码,假想一下每个方法函数的返回值都返回object对象,有n多种方法,然后就这么写

     double val = tb_val.Rows[i][0].ToString().ToDouble().ToNull().
    

    这样一来,代码不管是阅读还是编写都很流畅。要声明的是,这个理解不专业,但是足够用了。


    普遍的JS语法

    • describe
    • it
    • beforeAll, beforEach, afterAll, afterEach

    看到团队代码里面有这些语法内容,不甚了解。所以也一并记录在这里。

    descibe

    describe是Jasmine的全局函数,Jasmine是JS的一种单元测试框架。describe的参数有两个:1)字符串-作为测试内容名字或者标题;2)包含实现测试的代码

    describ(string, function(){
         //pass  
    }
    );
    

     可以理解成,string就是一个user story的名字,function就是这个user story的故事情节。当然,describe可以嵌套,就是一个大故事里有很多的章节。

     it

    it也是Jasmine的全局函数。it的参数也是两个:1)字符串;2)测试的主体

    it(string, function(){
        //pass
    });
    

    it可以理解成一个故事中的一段实质的故事内容,人物对话,动作行为等等,都是在it中发生。所以it中自有的变量是私有变量,it通常包含在一个describe中。

    expect

    前面介绍Protractor的时候有提到过expect,elementFinder对象发出交互指令后,会得到一个promise对象,这个对象的值无法直接获取(动作无所谓啦),只能通过1)then;2)expect来操作。其中then方法也是取值,只有expect才是方法命令。expect也可以作用在it中,就是对行为的断言,返回结果true/false。关于expect的断言方法还有很多,详见参考资料。https://www.ibm.com/developerworks/cn/web/1404_changwz_jasmine/

    Setup与Teardown:beforeEach,beforeAll,afterEach与afterAll

    在执行测试用例时,会准备大量的工作。建立数据库,建立用户交互场景等等,同时测试完成后,我们也需要释放资源。如果对一个user story(如果很长、很复杂的话)中每个行为都如此准备,显然代码很冗余、模块的复用性也不好。因此可以使用setup和teardown,前者是测试之前的准备工作,后者是测试之后的清理工作。Jasmine中共有4个全局函数来执行这两种操作。

    beforeAll  [在测试套件(describe块)中所有测试用例执行之前执行一遍beforeAll函数]
    beforeEach  [在每一个测试用例(it块)执行之前都执行一遍beforeEach 函数]
    afterEach  [在每一个测试用例(it块)执行之后都执行一遍afterEach 函数]
    afterAll   [在测试套件(describe块)中所有测试用例执行之后执行一遍afterAll函数]
    

    这些函数都只有一个输入参数,就是内容的执行函数。这些函数和it块之间有运行的逻辑先后,理解它很有必要。  

    desribe("Check the priority", function(){
        beforAll(function(){
            console.log('this is beforall');
        });
        beforeEach(function(){
            console.log('this is beforeeach');
        });
        it("It1",function(){
            console.log('I am the 1st it');
        });
        it("It2",function(){
            console.log('I am the 2nd it');
        });
        describe("I am the child story",function(){
            console.log('I am the child story');
        });
        it("It3",function(){
            console.log('I am the 3rd it');
        });
        console.log('I am the parent story');
        afterEach(function(){
            console.log('this is the aftereach');
        });
        afterAll(function(){
            console.log('this is the afterAll');
        });
    });
    View Code

     以上测试代码的结果是:

    I am the parent story
    this is beforall
    this is beforeeach
    I am the 1st it
    this is the aftereach
    this is beforeeach
    I am the 2nd it
    this is the aftereach
    this is beforeeach
    I am the child story
    this is the aftereach
    this is beforeeach
    I am the 3rd it
    this is the aftereach
    this is the afterAll
    View Code

     所以比较清楚了,describe中不属于任何it块或者setup/teardown的内容先运行。然后是beforall。之后从上往下找到第一个it(嵌套的describe这时也属于一个it),前面是beforeeach,再运行it的代码,最后是aftereach的代码。接着再是第二个it,前后分别是beforeeach和aftereach。之后的it同样如此。最后是afterall。  


    参考资料:

    1. 使用protractor操作页面元素
    2. Jasmine中的describe和it
    3. JavaScripy单元测试框架:Jasmine初探
    4. Jasmine的断言
    5. 链式调用资料1
    6. 链式调用资料2
  • 相关阅读:
    深入Android 【一】 —— 序及开篇
    Android中ContentProvider和ContentResolver使用入门
    深入Android 【六】 —— 界面构造
    The service cannot be activated because it does not support ASP.NET compatibility. ASP.NET compatibility is enabled for this application. Turn off ASP.NET compatibility mode in the web.config or add the AspNetCompatibilityRequirements attribute to the ser
    Dynamic Business代码片段总结
    对文件的BuildAction以content,resource两种方式的读取
    paraview 3.12.0 windows下编译成功 小记
    百度网盘PanDownload使用Aria2满速下载
    netdata的安装与使用
    用PS给证件照排版教程
  • 原文地址:https://www.cnblogs.com/RicardoIsLearning/p/12955022.html
Copyright © 2011-2022 走看看