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

时间:2012-06-20 19:30:06

标签: ruby selenium webdriver selenium-webdriver pageobjects

第一次发布海报。我已经在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 -

6 个答案:

答案 0 :(得分:30)

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

  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',即:'Single Responsibility Principle'(SRP)。一个班级应该对一件事负责,而且只对一件事负责。在这种情况下,页面对象类应该负责对页面(或其部分)进行建模,仅此而已。如果它不止于此(例如:包括断言),那么你就违反了SRP。

答案 1 :(得分:10)

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

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

答案 2 :(得分: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(旧,新)     catch elementNotFound

或不抛出异常

page = site.OpenProfilePage
if ! page instanceof ProfilePage

或复杂的检查

assert page.looksLikeAProfilePage

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

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

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

答案 3 :(得分: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()) 

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

答案 4 :(得分:3)

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

回到原始问题,如果你真的必须在Page Object中做断言,为什么不使用selenium loadablecomponent<>您可以在其中使用isLoaded()方法或在loadablecomponent<>中包含您的自定义断言类。这将使您的页面对象不受断言。但您可以在可加载组件中执行断言。见下面的链接......

http://code.google.com/p/selenium/wiki/LoadableComponent

_梦想家

答案 5 :(得分:-1)

我完全不同意作者。

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

我真的不相信这会增加执行时间。默认情况下,UI自动化速度很慢,添加几毫秒的检查并不会带来太大的改变,但可以简化您的故障排除,报告早期故障并提高代码的可重用性。

但是,它也取决于UI测试的类型。例如,如果您要使用大部分为正数的路径来实施端到端测试,则有必要在测试方法中进行检查,即单击按钮实际上会导致打开页面。但是,如果您要编写大量负面的情况,则并非总是如此。