• nightwatch系列教程04——开发者指南:在测试中使用Page Object


    本章内容翻译自http://nightwatchjs.org/guide#page-objects

    使用 Page Objects

    Page Objects方法是一种流行的模式,通过将web应用程序的页面或页面片段包装为对象来编写端到端测试。页面对象的目的,是通过抽象出html的行为,让软件客户端可以像人一样的看见并且操作界面。关于Page Object,你可以在这里找到更详细的介绍。


    从0.7版本开始,Nightwatch为创建页面对象提供了增强的、更强大的接口,与以前的支持相比有了显著的改进。0.7之前创建的页面对象仍将继续工作,不过我们建议升级到新版本。要使用新版本,Page Object必须包含 `elements` 或 `sections` 属性。否则,会按照旧版做处理。

    配置 Page Object

    简单地创建一个页面对象,只需要用描述页面的属性创建一个对象就可以了。每一个页面对象都应该属于不同的文件, Nightwatch将读取配置文件中page_objects属性定义的目录。

    page_objects也可以是一个文件夹数组,让你可以根据业务逻辑将页面对象分成不同的组。

    Url 属性

    你可以添加一个 url 属性来指定页面的url。你可以调用 navigate() 方法来跳转到对应的界面。

    URL 通常是个字符串:

    module.exports = {
      url: 'http://google.com',
      elements: {}
    };

    他也可以是一个函数, 用来生成动态的链接。比如你想要支持不同的测试环境的时候这就很有用。你可以创建一个函数,他在页面的上下文中被调用:

    module.exports = {
      url: function() { 
        return this.api.launchUrl + '/login'; 
      },
      elements: {}
    };

    ### 定义元素(elements)

    多数情况下你都会在你的页面上定义元素,你的测试将会通过命令或者断言来与他交互。使用elements属性可以让你的元素都定义在同一个地方。

    Page Object 内部会自动等切换css和xpath,所以你不用在测试中手动调用 useXpath 或者 useCss 。默认的 locateStrategy 是css,但你仍可以指定xpath:

    module.exports = {
      elements: {
        searchBar: { 
          selector: 'input[type=text]' 
        },
        submit: { 
          selector: '//[@name="q"]', 
          locateStrategy: 'xpath' 
        }
      }
    };

    如果你的elements都是使用同一种默认选择器策略的话,你可以使用这种简写形式:

    module.exports = {
      elements: {
        searchBar: 'input[type=text]'
      }
    };

    个人提示:上面的简写,我本人在项目中试过了,不起作用,而且报的错误也莫名其妙。如果你在使用中发现错误,不要使用简写形式试试

    在调用元素命令或者断言的时候,elements 属性可以让你使用 @ 作为前缀来引用定义的元素。

    你也可以定义一个元素对象的数组:

    var sharedElements = {
      mailLink: 'a[href*="mail.google.com"]'
    };
    
    module.exports = {
      elements: [
        sharedElements,
        { searchBar: 'input[type=text]' }
      ]
    };

    看一个包含 urlelements 属性的例子:

    module.exports = {
      url: 'http://google.com',
      elements: {
        searchBar: { 
          selector: 'input[type=text]' 
        },
        submit: { 
          selector: '//[@name="q"]', 
          locateStrategy: 'xpath' 
        }
      }
    };

    测试文件可以这么写:

    module.exports = {
      'Test': function (client) {
        var google = client.page.google();
    
        google.navigate()
          .assert.title('Google')
          .assert.visible('@searchBar')
          .setValue('@searchBar', 'nightwatch')
          .click('@submit');
    
        client.end();
      }
    };

    定义 Sections

    有时候将页面划分为几部分(Section)也非常有用。Section有两个好处:

    • 在页面上提供了一个命名空间的层级
    • 提供元素级别的嵌套,即 Section 中的 elements 对应为它在的DOM中的子元素

    使用 sections 属性来定义:

    module.exports = {
      sections: {
        menu: {
          selector: '#gb',
          elements: {
            mail: {
              selector: 'a[href="mail"]'
            },
            images: {
              selector: 'a[href="imghp"]'
            }
          }
        }
      }
    };

    测试文件这么写:

    module.exports = {
      'Test': function (client) {
        var google = client.page.google();
        google.expect.section('@menu').to.be.visible;
    
        var menuSection = google.section.menu;
        menuSection.expect.element('@mail').to.be.visible;
        menuSection.expect.element('@images').to.be.visible;
    
        menuSection.click('@mail');
    
        client.end();
      }
    };


    注意:section上每一个命令(command)和断言(assertion,不是expect assertions)都返回其自身——链式返回。如果需要的话,你可以为复杂的DOM结构编写嵌套的section。

    一个嵌套section的例子:

    module.exports = {
      sections: {
        menu: {
          selector: '#gb',
          elements: {
            mail: {
              selector: 'a[href="mail"]'
            },
            images: {
              selector: 'a[href="imghp"]'
            }
          },
          sections: {
            apps: {
              selector: 'div.gb_pc',
              elements: {
                myAccount: {
                  selector: '#gb192'
                },
                googlePlus: {
                  selector: '#gb119'
                }
              }
            }
          }
        }
      }
    };

    在测试文件中使用嵌套section:

    module.exports = {
      'Test': function (client) {
        var google = client.page.google();
        google.expect.section('@menu').to.be.visible;
    
        var menuSection = google.section.menu;
        var appSection = menuSection.section.apps;
        menuSection.click('@appSection');
    
        appSection.expect.element('@myAccount').to.be.visible;
        appSection.expect.element('@googlePlus').to.be.visible;
    
        client.end();
      }
    };

    编写命令(Command)

    你可以在page object中使用 command 属性来添加命令。这是一个封装页面业务逻辑的一个有用的方式。
    Nightwatch 将会调用页面上下文或者section的命令。客户端的命令(比如 pause)你可以使用 this.api 来访问。为了链式调用,每个命令函数都应该返回page对象或者section

    在下面的案例中,这个命令用来封装点击提交按钮的逻辑:

    var googleCommands = {
      submit: function() {
        this.api.pause(1000);
        return this.waitForElementVisible('@submitButton', 1000)
          .click('@submitButton')
          .waitForElementNotPresent('@submitButton');
      }
    };
    
    module.exports = {
      commands: [googleCommands],
      elements: {
        searchBar: {
          selector: 'input[type=text]'
        },
        submitButton: {
          selector: 'button[name=btnG]'
        }
      }
    };

    测试文件就简单多了:

    module.exports = {
      'Test': function (client) {
        var google = client.page.google();
        google.setValue('@searchBar', 'nightwatch')
          .submit();
    
        client.end();
      }
    };
  • 相关阅读:
    iOS:不同属性声明方式的解析
    iOS:图像和点击事件
    iOS:NAV+TABLE结合
    iOS:实现表格填充和选择操作
    iOS: 填充数据表格
    iOS:导航栏的工具条和导航条
    iOS:使用导航栏
    hello,world不使用ARC
    iOS代码实现:创建按钮,绑定按钮事件,读取控件值
    iOS版 hello,world版本2
  • 原文地址:https://www.cnblogs.com/jerryyj/p/9621542.html
Copyright © 2020-2023  润新知