量角器中的嵌套页面对象

ale*_*cxe 17 javascript selenium project-organization pageobjects protractor

问题:

在Protractor中定义嵌套页面对象的规范方法是什么?

使用案例:

我们有一个复杂的页面,由多个部分组成:过滤器面板,网格,摘要部分,侧面的控制面板.将所有元素和方法定义放在单个文件和单个页面对象中不起作用和扩展 - 它变得一团糟,难以维护.

Ram*_*ala 11

当涉及页面对象以及如何维护它们时,这更像是一个主题.不久之后,我偶然发现了一个我喜欢的页面对象设计模式技术,对我来说很有意义.

不是在父页面对象中实例化子页面对象,而是遵循javascript的Prototypal Inheritance概念.这有很多好处,但首先让我展示一下我们如何实现它:

首先,我们将创建我们的父页面对象ParentPage:

// parentPage.js
var ParentPage = function () {
// defining common elements
this.someElement = element(by.id("someid"));

// defining common methods
ParentPage.prototype.open = function (path) {
browser.get('/' + path)
}
}

module.exports = new ParentPage();  //export instance of this parent page object
Run Code Online (Sandbox Code Playgroud)

我们将始终导出页面对象的实例,并且永远不会在测试中创建该实例.由于我们正在编写端到端测试,因此我们始终将页面视为无状态构造,就像每个http请求是无状态构造一样.

现在让我们创建子页面对象ChildPage,我们将使用Object.create方法继承父页面的原型:

//childPage.js
var ParentPage = require('./parentPage')
var ChildPage = Object.create(ParentPage, {
/**
 * define elements
 */
username: { get: function () { return element(by.css('#username')); } },
password: { get: function () { return element(by.css('#password')); } },
form:     { get: function () { return element(by.css('#login')); } },
/**
 * define or overwrite parent page methods
 */
open: { value: function() {
    ParentPage.open.call(this, 'login'); // we are overriding parent page's open method
} },
submit: { value: function() {
    this.form.click();
} }
});
module.exports = ChildPage
Run Code Online (Sandbox Code Playgroud)

我们在getter函数中定义定位器.这些函数在您实际访问属性时进行评估,而不是在生成对象时进行评估.有了这个,你总是在对它做一个动作之前请求它.

该Object.create方法返回该页面的实例,以便我们可以立即开始使用它.

// childPage.spec.js
var ChildPage = require('../pageobjects/childPage');
describe('login form', function () {
it('test user login', function () {
    ChildPage.open();
    ChildPage.username.sendKeys('foo');
    ChildPage.password.sendKeys('bar');
    ChildPage.submit();
});
Run Code Online (Sandbox Code Playgroud)

请注意,我们只需要子页面对象并在我们的规范中使用/覆盖父页面对象.以下是此设计模式的好处:

  • 删除父页面对象和子页面对象之间的紧耦合
  • 促进页面对象之间的继承
  • 延迟加载元素
  • 封装方法和行动
  • 更清洁,更容易理解元素关系,而不是 parentPage.childPage.someElement.click();

我在webdriverIO的开发者指南中找到了这个设计模式,我解释的大多数上述方法都是从该指南中获取的.随意探索它,让我知道你的想法!

  • 在你提到的文章中指出 - "首先我们需要一个我们称之为Page的主页面对象.它将包含所有页面对象将继承的通用选择器或方法." 我认为它不涉及包含可以跨页面重用或具有太多逻辑的组件的"大"页面.也不确定继承是否会减少耦合.它实际上使它紧密耦合.虽然原型继承与java等传统继承概念不同...... (3认同)

ale*_*cxe 5

这个想法是将Page Object定义为一个包 - 目录index.js作为入口点.父页面对象将充当子页面对象的容器,在这种情况下,子页面对象具有"屏幕的一部分"含义.

父页面对象将在其中定义index.js,它将包含所有子页面对象定义,例如:

var ChildPage1 = require("./page.child1.po"),
    ChildPage2 = require("./page.child2.po"),

var ParentPage = function () {
    // some elements and methods can be defined on this level as well
    this.someElement = element(by.id("someid"));

    // child page objects
    this.childPage1 = new ChildPage1(this);
    this.childPage2 = new ChildPage2(this);
}

module.exports = new ParentPage();
Run Code Online (Sandbox Code Playgroud)

请注意如何this将其传递到子页面对象构造函数.如果子页面对象需要访问父页面对象的元素或方法,则可能需要这样做.

子页面对象看起来像这样:

var ChildPage1 = function (parent) {
    // element and method definitions here
    this.someOtherElement = element(by.id("someotherid"));
}

module.exports = ChildPage1;
Run Code Online (Sandbox Code Playgroud)

现在,使用这种页面对象会非常方便.您只需要父页面对象并使用点表示法来访问子页面对象:

var parentPage = requirePO("parent");

describe("Test Something", function () {
    it("should test something", function () {
        // accessing parent
        parentPage.someElement.click();

        // accessing nested page object
        parentPage.childPage1.someOtherElement.sendKeys("test");
    });
});
Run Code Online (Sandbox Code Playgroud)

requirePO() 是一个缓解进口的辅助功能.


从我们的一个测试自动化项目中获取嵌套页面对象目录结构示例:

在此输入图像描述