37

第一次海报。我从事 UI 自动化工作多年,但最近才被介绍/指导使用页面对象模型。其中大部分是常识,包括我已经使用过的技术,但有一个特别好的点我无法在自己的脑海中证明,尽管广泛搜索了一个合理的解释。我希望这里有人可以启发我,因为当我尝试将 POM 与我自己的最佳实践集成时,这个问题引起了一些恐慌。

来自http://code.google.com/p/selenium/wiki/PageObjects

上面给出的代码显示了一个重要的点:测试,而不是 PageObjects,应该负责做出关于页面状态的断言......当然,与每条指南一样,也有例外......

来自http://seleniumhq.org/docs/06_test_design_considerations.html#chapter06-reference

页面对象的设计方式有很大的灵活性,但是有一些基本规则可以让您的测试代码获得所需的可维护性。页面对象本身不应该进行验证或断言。这是测试的一部分,应该始终在测试代码中,而不是在页面对象中。页面对象将包含页面的表示,以及页面通过方法提供的服务,但与正在测试的内容相关的代码不应包含在页面对象中。

有一个单一的验证可以而且应该在页面对象内,即验证页面以及页面上可能的关键元素是否正确加载。此验证应在实例化页面对象时完成。

这两个“指导方针”都允许潜在的例外情况,但我完全不同意基本前提。我习惯于在“页面方法”中进行大量验证,并且我认为验证的存在是一种在各种上下文中查找问题的强大技术(即,每次调用方法时都会进行验证)而不是而不仅仅是在特定测试的有限环境中发生。

例如,假设当您登录到 AUT 时,会出现一些文本,上面写着“以用户身份登录”。有一个单独的测试来专门验证它是合适的,但你为什么不想每次都验证它登录叫什么?这个工件与页面是否“加载正确”没有直接关系,一般来说也与“正在测试什么”没有关系,所以根据上面的POM指南,它显然不应该在页面方法中。 .. 但在我看来,它显然应该存在,通过尽可能多地验证重要工件,尽可能少地预先考虑,从而最大限度地发挥自动化的力量。将验证码放入页面方法中,通过允许您“免费”获得大量验证,而无需在测试中担心它,从而增加了自动化的力量,并且在不同的上下文中进行如此频繁的验证通常会发现您找不到的问题如果验证仅限于,例如,对该工件的单一测试。

换句话说,我倾向于区分特定于测试的验证和“一般”验证,并且我认为将后者广泛包含在页面方法中是完全合适/理想的。这促进了更精简的测试和更厚的页面对象,这通常通过重用更多代码来提高测试的可维护性——尽管这些指南中存在相反的争论。我错过了重点吗?不想在页面方法中进行验证的真正理由是什么?我描述的情况实际上是这些指南中描述的“例外”之一,因此实际上与 POM 不一致吗?提前感谢您的想法。-jn-

4

6 回答 6

37

作为准则,断言应该在测试中而不是在页面对象中完成。当然,有时这不是一种务实的方法,但这些时候并不常见,以至于上述指导方针是正确的。以下是我不喜欢在页面对象中使用断言的原因:

  1. verify阅读一个只调用断言隐藏在页面对象中其他地方的方法的测试是非常令人沮丧的。在可能的情况下,测试断言的内容应该很明显;当断言直接在测试中时,这是最好的实现。通过将断言隐藏在测试之外的某个地方,测试的意图并不那么清楚。

  2. 浏览器测试中的断言可能很昂贵——它们确实会减慢您的测试速度。当您有数百或数千个测试时,可以将分钟/小时添加到您的测试执行时间;这是一件坏事。如果您将断言移动到只关心那些特定断言的测试,您会发现您将拥有更快的测试,并且您仍然会发现相关的缺陷。问题包括以下内容:

    将验证码放入页面方法中,让您“免费”获得大量验证,从而使自动化的力量倍增

    好吧,“自由不是免费的” :) 你实际上增加的是你的测试执行时间。

  3. 到处都有断言违反了另一个好的准则;“每个测试一个断言”(http://blog.jayfields.com/2007/06/testing-one-assertion-per-test.html)。我不虔诚地坚持它,但我努力遵循这个原则。在可能的情况下,测试应该只对一件事感兴趣。

  4. 测试的价值降低了,因为一个错误会导致大量测试失败,从而阻止他们测试他们应该测试的内容。

    例如,假设当您登录到 AUT 时,会出现一些文本,上面写着“以用户身份登录”。让一个测试专门验证这一点是合适的,但是为什么您不想在每次调用 login 时验证它呢?

    如果您在页面对象类中有断言并且预期的文本发生更改,则所有登录的测试都将失败。相反,如果断言在测试中,那么只有一个测试会失败 - 专门测试正确消息的测试 - 让所有其他测试继续运行以查找其他错误。你不需要 5000 次测试就可以告诉你登录信息是错误的;1个测试就可以了;)

  5. 让一个班级做不止一件事违反了SOLID中的“S” ,即:“单一责任原则”( SRP)。一个类应该负责一件事,而且只负责一件事。在这种情况下,页面对象类应该负责为页面(或其部分)建模,仅此而已。如果它的作用不止于此(例如:包括断言),那么您就违反了 SRP。

于 2012-07-20T10:50:09.843 回答
12

我有时也为这个建议而苦苦挣扎。我相信这个指南背后的原因是为了让你的页面对象保持可重用性,并且在你的页面对象中放置断言可能会限制它们被大量不相关的测试重用的能力。也就是说,我已经在我的页面对象上放置了某些验证方法,例如测试标题的标题 - 根据我的经验,这是一种更好的方法来封装页面中不会更改的元素的测试逻辑。

另一个注意事项 - 我已经看到 MVC 应用程序将域模型重用为页面对象。如果做得正确,这可以显着减少测试库中的冗余代码。使用这种模式,视图模型没有对测试框架的引用,因此很明显,您不能在其中放置任何断言。

于 2012-06-20T20:34:53.650 回答
6

您的页面对象不应执行断言,因为页面对象必须了解您的测试框架(除非您使用内置语言断言)。但是您的页面需要知道它的状态才能定位元素并执行操作。

关键在于“当然,正如每条指南都有例外......”

您的页面应该抛出异常,而不是执行断言。这样,您的测试可以捕获断言并保释或采取相应措施。例如。

page = ProfilePage.open
try 
  page.ChangePassword(old, new)
catch notLoggedIn
  page.Login(user, pass)

assert page.contains "your password has been updated"

在这个有限的示例中,您必须再次(一次又一次)检查,因此它可能不是最好的方法,但您明白了。你也可以只检查状态(两次)

if page.hasLoginDialog
  page.Login

if page.hasLoginDialog //(again!)
  assert.fail("can't login")

您也可以只检查您是否有个人资料页面

try 
  page = site.OpenProfilePage
catch notOnProfilePage

或者有你需要的元素试试 profilepage.changePassword(old,new) catch elementNotFound

或不抛出异常

page = site.OpenProfilePage
if ! page instanceof ProfilePage

或复杂的检查

assert page.looksLikeAProfilePage

重要的不是你怎么做。您希望将测试中的逻辑保持在最低限度,但您不希望将页面对象绑定到您的测试框架——毕竟,您可能使用相同的对象进行抓取或数据生成——或者使用不同的测试有它自己的断言的框架。

如果你觉得有必要,你可以将你的断言从你的测试用例中推到测试辅助方法中。

page = site.GoToProfilePage
validate.looksLikeProfilePage(page)

如果您的语言支持它们,这是一个很好的混入机会,因此您可以拥有干净的页面对象 - 并混入您的健全性检查。

于 2012-10-11T21:26:01.427 回答
4

当我看到可以在多个测试方法中使用相同的断言时,这让我感到困惑。例如,编写断言特定方法 -

public PaymentPage verifyOrderAmount(BigDecimal orderAmount) {      
   Assertion.assertEquals(lblOrderAmount.getText(), orderAmount, "Order Amount is wrong on Payment details page"); 
   return this; 
}

现在我可以在我需要的所有测试中重用它。而不是在处理多个场景的多个测试中重复相同的断言语句。不用说,我可以根据测试在一个方法中链接多个断言,即 -

 .verifyOrderAmount(itemPrice)
 .verifyBankAmount(discountedItemPrice)
 .verifyCouponCode(flatDiscountCode.getCouponCode()) 

那么当页面对象应该代表页面提供的服务时,断言点不也是页面提供的服务吗?

于 2013-02-11T05:24:30.493 回答
3

@Matt 在页面对象中重用域模型可能会节省您的时间,但这不是测试气味,测试逻辑对域模型很清楚(取决于您要实现的目标)。

回到最初的问题,如果你真的必须在页面对象中进行断言,为什么不使用 selenium loadablecomponent<> ,你可以使用 isLoaded() 方法或在 loadablecomponent<> 类中包含自定义断言。这将使您的页面对象免于断言。但是您可以在可加载组件中进行断言。请看下面的链接...

https://github.com/SeleniumHQ/selenium/wiki/LoadableComponent

_梦想家

于 2012-12-12T10:26:19.380 回答
-1

我完全同意作者的看法。

在测试方法中添加断言可以帮助您“尽早失败”。通过断言,我的意思是检查单击按钮后是否加载了某个页面等(所谓的一般断言)。

我真的不相信它会增加执行时间。默认情况下,UI 自动化很慢,添加一些几毫秒的检查并没有太大的区别,但可以简化您的故障排除、报告早期故障并使您的代码更具可重用性。

但是,它也取决于 UI 测试的类型。例如,如果您正在实施具有大部分正路径的端到端测试,那么在测试方法中检查单击按钮实际上会导致打开页面是有意义的。但是,如果您正在编写一堆负面场景,情况并非总是如此。

于 2019-09-30T14:42:51.130 回答