页面对象模型:为什么不在页面方法中包含断言?

Jon*_*son 34 ruby selenium webdriver pageobjects selenium-webdriver

第一次海报.我已经在UI自动化领域工作了很多年,但最近才引入/指示使用Page Object Model.其中大部分是常识,包括我已经使用的技术,但有一个特别好的点我无法在自己的脑海中证明,尽管广泛搜索了一个合理的解释.我希望这里有人可以启发我,因为这个问题引起了一些惊愕,因为我试图将POM与我自己的最佳实践相结合.

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

上面给出的代码显示了一个重要的观点:测试而不是PageObjects应该负责对页面状态进行断言....当然,正如每个指南都有例外......

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

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

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

这两个"指南"都允许潜在的例外,但我对基本前提不能不同意.我习惯于在"页面方法"中进行大量的验证,我认为验证的存在是一种强大的技术,可以在各种环境中查找问题(即每次调用方法时都会进行验证)而不仅仅发生在特定测试的有限环境中.

例如,让我们想象一下,当您登录AUT时,会出现一些文本"以USER身份登录".有一个测试专门验证这个是合适的,但为什么不想在每次调用登录时验证它?这个工件与页面"是否正确加载"没有直接关系,并且它与"正在测试的内容"无关,因此根据上面的POM指南,它显然不应该是页面方法. ..但在我看来,它显然应该存在,通过尽可能经常地验证重要的工件来最大化自动化的力量,尽可能少的事先考虑.将验证码放入页面方法可以增加自动化的功能,允许您"免费"获得大量验证,而不必在测试中担心,并且在不同的环境中进行频繁验证通常会发现您不会发现的问题如果验证仅限于对该工件的单个测试.

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

Nat*_*yer 32

作为指导原则,断言应该在测试中完成,而不是在页面对象中完成.当然,有时这不是一种务实的方法,但这些时间很少,上述指南是正确的.以下是我不喜欢在页面对象中使用断言的原因:

  1. 读取一个只调用verify断言被埋在页面对象中的其他地方的方法的测试是非常令人沮丧的.在可能的情况下,测试的主张应该是显而易见的; 当断言直接在测试中时,这是最好的.通过将断言隐藏在测试之外的某个地方,测试的目的不是那么清楚.

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

    将验证码放入页面方法可以增加自动化的功能,让您"免费"获得大量验证

    好吧,"自由不是免费的":)你实际上乘以你的测试执行时间.

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

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

    例如,让我们想象一下,当您登录AUT时,会出现一些文本"以USER身份登录".有一个测试专门验证这个是合适的,但为什么不想在每次调用登录时验证它?

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

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

  • 每次测试的一个断言对于单元测试是有利的,但对于系统测试则没有那么多.正如我在之前的评论中所说,测试操作(例如页面加载)需要2秒,断言需要10毫秒.在测试操作之后执行N个断言比编写N个测试并在每个测试之后进行1次断言要快得多 (3认同)
  • “你实际乘以的是你的测试执行时间。” 假设页面加载时间 = 2 秒。一项基本断言大约为 10 毫秒(在 Firefox 中)。在每次页面加载后执行一次断言可以使测试执行时间增加不到 1%,而使我快速找出错误的能力增加超过 1%。我的观点是测试应该很快失败 (2认同)
  • 当然,您应该对每个测试保持一个断言,否则测试会由于模棱两可的原因而失败,这将浪费更多的时间来尝试准确地确定测试是如何失败的。 (2认同)

Mat*_*old 10

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

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

  • 我不认为在创建页面对象时需要遵循任何硬规则.目标是让事情可以重复使用,如果你能做到这一点,那么你就是在正确的轨道上. (3认同)
  • 我同意你的看法.我们可以在页面级别进行验证.但是,当你说"这是有道理的,但我可以设计我的页面方法可以广泛重复使用并同时进行一些有用的验证" - 我认为这取决于AUT和设计.我不希望我的EndToEnd测试失败而不执行大部分步骤只是因为登录后丢失了一个文本. (3认同)
  • >将断言置于页面对象中可能会限制它们被大量不相关的测试重用的能力 (2认同)

fij*_*ron 6

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

关键在于声明"当然,正如每条指南都有例外......"

您的页面应该抛出异常,而不是执行断言.这样你的测试可以捕获断言和保释或相应的行为.例如.

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

assert page.contains "your password has been updated"
Run Code Online (Sandbox Code Playgroud)

在这个有限的例子中,你必须再次检查(并再次),这可能不是最好的方法,但你明白了.你也可以检查状态(两次)

if page.hasLoginDialog
  page.Login

if page.hasLoginDialog //(again!)
  assert.fail("can't login")
Run Code Online (Sandbox Code Playgroud)

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

try 
  page = site.OpenProfilePage
catch notOnProfilePage
Run Code Online (Sandbox Code Playgroud)

或者你需要的元素尝试profilepage.changePassword(旧的,新的)catch elementNotFound

或者没有抛出异常

page = site.OpenProfilePage
if ! page instanceof ProfilePage
Run Code Online (Sandbox Code Playgroud)

或复杂的检查

assert page.looksLikeAProfilePage
Run Code Online (Sandbox Code Playgroud)

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

如果您有需要,可以将您的断言从测试用例中推送到测试助手方法中.

page = site.GoToProfilePage
validate.looksLikeProfilePage(page)
Run Code Online (Sandbox Code Playgroud)

如果你的语言支持mixin,那么这对mixin来说是一个很好的机会,所以你可以拥有干净的页面对象 - 并混合你的理智检查.


Tar*_*run 6

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

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

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

 .verifyOrderAmount(itemPrice)
 .verifyBankAmount(discountedItemPrice)
 .verifyCouponCode(flatDiscountCode.getCouponCode()) 
Run Code Online (Sandbox Code Playgroud)

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

  • 您有一个可重用验证断言的好例子。然而,与我们的测试助手一起使用会更好,它允许我们在实际测试方面重用方便的方法。 (3认同)