开发页面对象模型时,总体目的通常是将页面抽象为使代码可重复使用并提高可读性的方法。

我经常会卡住某个级别的代码我抽象了什么?例如,例如登录表单(或者实际上大多数表单)。 。下面的代码是JavaScript):

  setEmail(value) {
    const field = cy.get('.form-control[name="username"]');
    field.clear();
    field.type(value);

    return this;
  }

  setPassword(value) {
    const field = cy.get('.form-control[name="password"]');
    field.clear();
    field.type(value);

    return this;
  }


通常,在这一点上,我想知道“嗯,我应该继续将此形式n的输入包装到更大的方法中“:

 login(email, password) {
    this.setEmail(email);
    this.setPassword(password);
    this.submit();
  }


那我要问:“为什么还要打扰编写独立的输入法呢?”当值得编写一个包含多个其他私有方法的大型方法时...如果我编写一个较大的“公共”方法,我是否还要编写较小的私有方法?

我在PoM的某个地方读过应该:


模型用户行为而不是用户界面


当我想到该语句时,尤其是在形式方面,感觉就像一个“较大”方法使s恩斯。我看到将它们拆分的唯一真正优势是,如果我需要测试否定案例(例如,并非所有输入都已填写)。从技术上讲,可以有条件地将其添加到较大的表格中(但是在有多个输入的情况下也可能会很痛苦)。

评论

上面提到的问题也不也是你的吗?

我最近在Stack Overflow上问了一个类似的问题,您可能对答案stackoverflow.com/questions/57251094/…感兴趣。

#1 楼

与任何软件一样,您可以在确定抽象时考虑一些启发式方法:

1-在更高级别的抽象中,单一职责原则:



组件(方法,类,模块)应充当单个角色。


意味着组件应出于单个原因而更改。

例如,如果您拥有一个包含logingoToAboutPage的HomePage对象,您可能会遇到问题(除非它只是一个Façade)。为什么?因为在同一个组件上,更改login的执行方式可能会影响goToAboutPage的执行方式。

您可以通过将这两个职责转移到两个不同的组件来解决它。

2-柯里定律:(一个函数应该做一件事)

一个函数应该做一件事,并且其名称应该清楚地说明它。它避免了添加意外结果和复杂错误的复杂性,而这些错误却又使您无法完成一件事。



如何实现它? />

如果可以将一个函数的一部分提取到另一部分,则您的函数可以做的事情不止一件事。 >
以下内容明确说明了您的登录方式:

login(email, password) {
    this.setEmail(email);
    this.setPassword(password);
    this.submit();
}


以下内容明确说明了如何设置电子邮件: br />
以下内容清楚地说明了如何查找电子邮件输入:键入行为。

您的代码将看起来像散文,这对您的读者(其他程序员和您未来的自我)很有好处。而且,由于一个人只能在3-4行代码函数中放置这么多错误,因此很容易发现错误。

评论


我不太清楚。您是在说我的登录功能/方法设置正确还是错误?

– Merfh
20-2-11在5:38

这里没有正确/错误。正如我说的,我提到的技巧是启发式的,容易出错的指导方针,以取得成果。如果您的方法有效,请尝试应用这些想法以进行改进。如果您不能应用它们以使事情变得更好,那么您就完成了。也许有一天你会看到如何改进-不要陷入渐近完美主义的境地。

–JoãoFarias
20-2-11在6:21

#2 楼

我不会说PoM模式旨在为行为建模。而是一个用户界面。测试旨在为行为建模。

如果您的对象不那么抽象,则可以更精确地声明结果。仅使用登录方法不会让您断言登录期间发生的事情。从技术上讲,我们可以做到,但是断言页面类中的任何内容都是不正确的做法。

所以我将行为分离到一个单独的类。

评论


这就是我要继续讲的重点。我的意思是,当我有非常大的表单(或复杂的表单)只是为了防止测试脚本过大时,它确实有帮助...但是当只有少数输入时,我看不到什么好处。话虽这么说...即使是大型的我仍然需要做其他断言。

– Merfh
20-2-10在2:22

另外,“将行为分离到一个单独的类”到底是什么意思?就像在非PoM课程中一样? (您介意举一个例子吗?)

– Merfh
20-2-10在5:09

所谓“在一个单独的类中解耦”,是指拥有一些与您的页面对象没有直接关系的代码(页面对象的一部分也不是任何派生的一部分),而是以“商业包装”。一个很好的例子可能是cumber框架中的“步骤定义”。

– Alexey R.
20-2-10在12:44

#3 楼

编写页面对象模型或任何测试框架时没有硬性规定。关键是确定可重用,易于理解且维护工作量少的方法。

我遵循的一些步骤是:


我麻烦写一个诸如login的包装函数吗?



如果不使用一个以上的测试用例,为什么要麻烦使用login函数。更多的函数调用实际上会减慢速度您的程序,因此,如果不重用它,则最好按原样使用它。
如果在1个以上的地方使用它,则可以使用诸如登录之类的包装器功能对其进行包装。这将减少代码行数并提高代码可重复使用性


我应该使用单独的方法吗? >这是一个棘手的部分,因为sendKeys(),click(),clear()已经是方法,为什么还要再次包装它?在您的情况下,这很有意义,因为您不仅要发送值,还要先清除它,然后发送它。但是,如果只是发送或单击,则不建议使用以下内容:

setEmail(value) {
    field.type(value);
}



但是从可读性和可维护性的角度来看,以下例如,在测试用例中常见的模式将很容易:

page1.goto()
page1.click('loginButton')
page1.sendValue('passwordField','test')





 let a= page1.goto();
 a.click(loginButton)
 a.sendkeys('test')

更好

评论


如果没有在超过1个测试用例中使用登录功能,为什么还要打扰。因为使用doLogin而不是一堆函数调用,使您的测试用例更加易于理解和维护。处理硒时,函数调用的开销完全没有意义。

–克莱里斯-谨慎乐观-
20-2-10在4:57

“更多的函数调用实际上会减慢您的程序的速度”,真的吗? softwareengineering.stackexchange.com/questions/80084/…

– Niels van Reijmersdal
20-2-10在11:00



我喜欢pageObjects隐藏所有定位符和框架用法。因此,如果我们更改测试工具,则只需更改pageObjects即可,而无需更改测试。您的page1示例将计数器login()方法。我希望pageObjects具有类似login()的方法。

– Niels van Reijmersdal
20-2-10在11:05

#4 楼

我喜欢更进一步,也像用户一样对测试数据进行抽象。我不喜欢在测试中对密码之类的东西进行硬编码。

将较大的方法拆分为较小的方法时,请遵循清晰的代码惯例。

#5 楼

他如何编写框架完全取决于个人。对于我来说,我建议将大的ui任务也分解为较小的步骤,并以较小的功能进行隐蔽处理。作为


这些小功能可以在需要时重用/调用,并且在负面测试用例中起着至关重要的作用,如果您
需要测试一个功能,以便您可以根据需要包装所有功能
,并转换成一个大函数以实现代码可重用性并完成
任务。


我将以问题登录为例。您可以简单地将其转换为小功能。功能)

setEmail(value) {
    const field = cy.get('.form-control[name="username"]');
    field.clear();
    field.type(value);

    return this;
  }

  setPassword(value) {
    const field = cy.get('.form-control[name="password"]');
    field.clear();
    field.type(value);

    return this;
  }


负测试用例-具有无效电子邮件和密码的测试登录功能(在这里您可以单独调用小功能)

login(email, password) {
    this.setEmail(email);
    this.setPassword(password);
    this.submit();
    // Apply assertion on home page
  }


负测试用例-使用无效电子邮件的测试登录功能(这里您可以单独调用小功能)

this.setEmail(incorrectEmail);
    this.setPassword(incorrect-password);
    this.submit();
// apply assertion on error message


负测试用例-测试登录功能带有无效密码(在这里您可以单独调用小功能)

this.setEmail(incorrectEmail);
    this.setPassword(password);
    this.submit();
// apply assertion on error message


负测试用例-使用空电子邮件和密码的测试登录功能(在这里您可以单独调用小功能)

this.setEmail(Email);
    this.setPassword(incorrectPassword);
    this.submit();
// apply assertion on error message



结论:小功能意味着我们具有更大的灵活性,因此我们可以根据需要使用它们。在上面的示例中,尽管我们可以使用具有错误和空凭据的login()
函数,但是在大多数情况下,小
函数可以根据条件进行单独调用,并且测试用例可以提供更大的灵活性


评论


到目前为止,这是我一直在做的事情。对于“快乐之路”,我将功能捆绑为一个较大的“ AddWidget”或“ Login”功能。但是,当我需要测试否定测试用例时,我也花时间添加额外的更小功能(因为尝试ALSO将否定测试用例合并到更大的功能中将很困难)。

– Merfh
20-2-10在4:46

#6 楼

没有良好抽象层的页面对象是垃圾,它破坏了许多编程规则,例如单一职责等。更好地开发具有组件的抽象层,然后在页面类中重用此组件。您可以创建共享组件和特定组件,这是Web开发中的类似方法,例如,当您使用React JS进行开发时就是常见方法。