页面对象的正确的Protractor语法是什么?

时间:2016-08-03 09:01:38

标签: protractor pageobjects

我为Protractor的页面对象遇到了不同类型的语法,我想知道,他们的背景是什么以及建议的方式。

这是Protractor教程的官方PageObject语法。我最喜欢它,因为它清晰可​​读:

use strict;

var AngularHomepage = function() {
  var nameInput = element(by.model('yourName'));
  var greeting = element(by.binding('yourName'));

  this.get = function() {
    browser.get('http://www.angularjs.org');
  };

  this.setName = function(name) {
    nameInput.sendKeys(name);
  };

  this.getGreeting = function() {
    return greeting.getText();
  };
};
module.exports = AngularHomepage;

但是,我也发现了这种情况:

'use strict';

var AngularPage = function () {
  browser.get('http://www.angularjs.org');
};

    AngularPage.prototype  = Object.create({}, {
      todoText:  {   get: function ()     { return element(by.model('todoText'));             }},
      addButton: {   get: function ()     { return element(by.css('[value="add"]'));          }},
      yourName:  {   get: function ()     { return element(by.model('yourName'));             }},
      greeting:  {   get: function ()     { return element(by.binding('yourName')).getText(); }},
      todoList:  {   get: function ()     { return element.all(by.repeater('todo in todos')); }},
      typeName:  { value: function (keys) { return this.yourName.sendKeys(keys);              }} ,
      todoAt:    { value: function (idx)  { return this.todoList.get(idx).getText();          }},
      addTodo:   { value: function (todo) {
        this.todoText.sendKeys(todo);
        this.addButton.click();
      }}
    });

    module.exports = AngularPage;

这两种方法的优点/缺点(可读性除外)是什么?第二个是最新的吗?我已经看到WebdriverIO使用了这种格式。

我还从Gitter的一个人那里听说第一个条目效率低下。有人可以向我解释原因吗?

2 个答案:

答案 0 :(得分:6)

页面对象模型 框架变得流行主要是因为:

  1. 减少代码重复
  2. 易于维护
  3. 高可读性
  4. 因此,我们通常会根据测试范围和需求开发测试框架(pom),方法是遵循适当的框架(pom)模式。没有这样的规则说严格我们应该遵循任何框架。

    注意:框架是为了让我们的任务变得简单,注重结果并且有效

    在你的情况下,第一个看起来很好而且容易。并且它在维护阶段不会导致混乱或冲突。

    示例第一个案例 - >元素定位器的声明发生在每个页面的顶部。如果将来任何元素定位器发生更改,将很容易更改。

    而在第二种情况中,在块级别中声明的定位器(在页面上散布)。如果将来需要识别和更改定位器,那将是一段时间。

    所以,根据以上几点选择你觉得舒服的那个。

答案 1 :(得分:3)

我更喜欢使用ES6类语法(http://es6-features.org/#ClassDefinition)。在这里,我准备了一些简单的例子,说明我如何使用ES6类和一些有用的技巧处理页面对象。

var Page = require('../Page')
var Fragment = require('../Fragment')

class LoginPage extends Page {
    constructor() {
        super('/login');
        this.emailField = $('input.email');
        this.passwordField = $('input.password');
        this.submitButton = $('button.login');

        this.restorePasswordButton = $('button.restore');
    }

    login(username, password) {
        this.email.sendKeys(username);
        this.passwordField.sendKeys(password);
        this.submit.click();
    }

    restorePassword(email) {
        this.restorePasswordButton.click();
        new RestorePasswordModalWindow().submitEmail(email);
    }
}

class RestorePasswordModalWindow extends Fragment {
    constructor() {
        //Passing element that will be used as this.fragment;
        super($('div.modal'));
    }

    submitEmail(email) {
        //This how you can use methods from super class, just example - it is not perfect.
        this.waitUntilAppear(2000, 'Popup should appear before manipulating');
        //I love to use fragments, because they provides small and reusable parts of page.
        this.fragment.$('input.email').sendKeys(email);
        this.fragment.$('button.submit')click();
        this.waitUntilDisappear(2000, 'Popup should disappear before manipulating');
    }
}
module.exports = LoginPage;

// Page.js
class Page {
    constructor(url){
        //this will be part of page to add to base URL.
        this.url = url;
    }

    open() {
        //getting baseURL from params object in config.
        browser.get(browser.params.baseURL + this.url);
        return this; // this will allow chaining methods.
    }
}
module.exports = Page;

// Fragment.js
class Fragment {
    constructor(fragment) {
        this.fragment = fragment;
    }

    //Example of some general methods for all fragments. Notice that default method parameters will work only in node.js 6.x
    waitUntilAppear(timeout=5000, message) {
        browser.wait(this.EC.visibilityOf(this.fragment), timeout, message);
    }

    waitUntilDisappear(timeout=5000, message) {
        browser.wait(this.EC.invisibilityOf(this.fragment), timeout, message);
    }
}
module.exports = Fragment;

// Then in your test:
let loginPage = new LoginPage().open(); //chaining in action - getting LoginPage instance in return.
loginPage.restorePassword('batman@gmail.com'); // all logic is hidden in Fragment object
loginPage.login('superman@gmail.com')