【问题标题】:Should elements ever be made available outside of a page object?是否应该在页面对象之外提供元素?
【发布时间】:2019-07-29 09:41:54
【问题描述】:

这是一个我找不到确切来源的问题,我希望根据用户以前的经验得到一些答案,主要是解释为什么某种方法没有行得通。

我正在通过 Protractor 使用 webdriver 进行自动化,并且正在就是否应该在页面对象本身之外提供页面元素进行辩论。经过研究,人们似乎采取了几种不同的方法,我无法完全掌握每种方法的长期影响。

我见过以下页面对象模型的不同实现:


定位器在页面对象中声明并导出

这是我最不喜欢的方法,因为它意味着元素实际上在测试中被识别。这似乎是一个不好的标准,因为它可能鼓励自动化程序直接在应用程序中使用新的定位器,而不是来自页面对象。 此外,任何需要动态信息的都不能在PO初始化时直接设置,需要进一步编辑。

pageobject.js

export default class HomePage {
    constructor() {
        this.passwordField = '#password';
        this.usernameField = '#user';
    }
}

test.js

const homePage = new HomePage();
$(homePage.usernameField ).sendKeys('admin');
$(homePage.passwordField ).sendKeys('password');

在页面对象中声明并导出的元素,定位器不是

pageobject.js

export default class HomePage {
    constructor() {
        this.passwordField = $('#password');
        this.usernameField = $('#user');
    }
}

test.js

const homePage = new HomePage();
homePage.usernameField.sendKeys('admin);
homePage.passwordField.sendKeys('password);

在页面对象中声明的元素,仅在页面对象中直接使用,仅导出方法

这是我过去使用的方法,我们最终得到了很多很多功能。例如,我们有 setUsename(), getCurrentUsername(), getUsernameAttibute(), verifyUsernameExists()password 元素和许多其他元素。我们的页面对象变得很大,所以我觉得这不再是最好的方法了。 然而,其中一个优点是我们的测试看起来非常干净且可读性很强。

pageobject.js

export default class HomePage {
    constructor() {
        var passwordField= $('#password');
        var usernameField = $('#user');
    }

    setUserName(name){
       username.sendKeys(name);
    };

    setPassword(password){
       passwordField.sendKeys(password);
    };
}

test.js

const homePage = new HomePage();
homePage.setUsername('admin');
homePage.setPassword('123');

我很想得到一些反馈,希望您能抽出时间阅读。

【问题讨论】:

  • 我喜欢到目前为止提交的两个答案,但我正在添加赏金以尝试生成其他一些观点。

标签: selenium-webdriver protractor webdriver automated-tests pageobjects


【解决方案1】:

我更喜欢并相信最后一种方法是最好的。

撇开我们谈论自动化这一事实不谈,任何优秀/出色的软件都具有以下特征。

  • 它由单个模块/部件/组件组成
  • 每个单独的模块/部件/组件在数据/信息(选择器、自动化情况下的 webdriver API 调用)方面都是内聚的,特定于其与数据交互的 API/方法。李>

最后一种方法提供了您所指出的测试清洁度的额外好处。

但是,大多数时候,无论出于何种原因,我们都倾向于忽略模块化,使现有的 PO 变得臃肿。这是我见过的并且是其中的一部分。因此,在某种程度上,PO 变得臃肿不是因为方法,而是自动化人员/测试人员/开发人员有意识地保持 PO 模块化、组合和更简单的方式。无论是关于 PO 还是关于应用程序功能代码都是如此

船运订单:

针对PO臃肿的问题,看看能不能把PO的共同元素分离出来。例如。页眉,页脚,左导航,右导航等在页面中很常见。它们可以被分离出来,并且 PO 可以由这些单独的部分/部分组成

即使在主要内容中,如果其合乎逻辑并且可以跨页面重用,则将公共内容(如果不是跨两个或多个页面,如果不是全部)分离到它们自己的组件 API 中。

自动化规范对 ex 执行广泛的回归是正常的。一个元素是一个密码字段,文本框的长度是这样等等。在这种情况下,为每个规范用例(或期望)添加方法是没有意义的。好消息是,这里的共性也占据了中心位置。基本上,提供跨规范使用的 API,而不是在一个规范中使用的 API。

例如。密码字段应该被屏蔽。您不太可能想在多个规范文件中进行测试。在这里,我们可以在LoginPO 中为它添加一个方法,例如isPasswordMasked() ,我们可以让密码字段从LoginPO 访问,并且规范对密码类型字段进行实际检查。通过这样做,我们仍然让LoginPO 控制对其他 API(login()logout() 等)重要的密码字段信息,即只有 PO 知道如何以及从何处获取该密码元素。具有将规范测试推送到规范文件的额外优势。

expect/assert的采购订单

在任何时候,将任何测试(或expect)作为 PO API 的一部分都不是一个好主意。原因:

  • PO 及其 API 可跨套件重复使用,任何人都应该易于查看和理解。他们的主要职责是提供通用 API。
  • 它们应该尽可能瘦(以防止胀气)
  • 更重要的是,浏览器自动化本身就比较慢。如果我们将测试逻辑添加到 PO 及其 API 方法中,我们只会让它变慢。

FWIW,我没有遇到任何 要求 臃肿 API 的网页。

在 PO 中公开元素:

这取决于我相信的用例。可能是仅在一个规范中使用的元素,也可能是要公开的基本情况。也就是说,总的来说,这个想法是规范应该对测试人员/开发人员以及稍后查看它们的人来说是可读的。无论是使用有意义的元素变量名还是方法名,充其量只是一种偏好。另一方面,如果一个元素需要进行一些交互(例如悬停打开菜单链接),那么它绝对是仅通过 API 公开的候选对象。

希望能增加一些说明!

【讨论】:

  • 我喜欢你提到的将页面对象分成不同部分的方法。您是否知道有关此方法的任何示例框架或文档,我可以在其中阅读更多信息?我把这个问题留了一段时间,希望能产生一些其他的观点
  • 我所采用的方法主要基于此答案和示例。我们将页面对象分解为组件对象,每个页面对象将在其构造函数中初始化每个组件对象。在每个页面/组件对象本身之外不会提供任何元素。我们将创建一个帮助程序类文件,每个页面/组件对象都将扩展该文件,以便帮助程序可以访问元素。我对这种方法很有信心,并有兴趣看看当我们开始大量自动化时它如何保持下去
【解决方案2】:

最后一种方式是实现页面对象的正确方式。页面对象背后的想法是它隐藏了页面的内部结构,并提供了一个干净的 API 供脚本调用以在页面上执行操作。定位器和元素不应暴露。您需要对页面执行的任何操作都应通过公共方法公开。

避免页面上每个字段的 getter 和 setter 的一种方法是合并方法。考虑用户将在页面上执行的操作。与其使用.setUsername().setPassword().clickLoginButton() 方法,不如使用login() 方法,该方法将用户名和密码作为参数并为您完成所有登录工作。

参考文献

Martin Fowler 通常被认为是“页面对象”概念的发明者,但其他人创造了“页面对象”这个名称。参见页面对象的his description

Selenium 在page objects 上的文档。

【讨论】:

  • 我遇到的唯一问题是,如果我们使用第三种方法,那么仅具有登录功能是不够的,因为我们将围绕用户名和密码字段进行功能测试。例如,我们可能需要验证密码是否被屏蔽,因此需要getAttributesForPassword(),或者我们可能需要验证用户名接受下划线,因此需要getUsername()。如果有很多这样的验证(在我的例子中是这样的),页面对象就变成了包含许多方法的数百行代码。
  • 同意。有时您所描述的内容是必要的。这并不意味着它仍然不是最好的方法。如果这 100 行代码没有放在该页面的页面对象中,它们应该放在哪里?
  • 您可以将许多阳性和阴性测试归结为loginAndExpectSuccess()loginAndExpectFailure()。将带有下划线的用户名传递给成功方法,您仍然应该能够登录。使用不受支持的字符传递您的用户名并期望被拒绝,以及如果您传递了错误的凭据或任何其他失败情况。
  • @JeffC 开始在这里回复,但随着回复越来越长,更新了回复。请在答案中查看我的第二次更新。
  • @RakeshKumarCherukuri 关于验证是否应该成为页面对象的一部分存在广泛争议,因此那里没有标准。话虽如此,我指的不是两种“预期”方法中的验证,而是指流程。成功登录会将您带到某个登录页面,登录失败的登录页面将停留在登录页面上并显示错误消息。
猜你喜欢
  • 2020-04-15
  • 2017-03-25
  • 1970-01-01
  • 2011-09-25
  • 1970-01-01
  • 2016-05-10
  • 1970-01-01
  • 2017-06-11
  • 1970-01-01
相关资源
最近更新 更多