5.8 页面对象(Page Object)模式
页面对象(Page Object)模式是目前自动化测试领域普遍使用的设计模式之一,此模式可以大大提高测试代码的复用率,提高测试脚本的编写效率和维护效率,是中级自动化测试工程师的必备技能之一。
1.页面对象模式简介
自动化测试脚本除了在一定程度上难编写,还有一个巨大的挑战,就是如何尽量减少维护的成本。大量的脚本因为难以维护,导致测试人员不得不再次投入大量资源去重新编写新的测试脚本,以满足日新月异的需求调整和变化。自动化测试脚本的巨大维护成本不仅会造成大量的人力投入,还会导致测试需求无法被快速响应,长此以往,会严重阻碍自动化测试在项目中的深入实施。
为了尽量解决上面的窘境,自动化测试脚本需要使用一些设计模式来降低测试脚本的工作量,提高自动化测试的投入产出比,延长自动化测试脚本的服务和工作周期。WebDriver提供了页面对象模式来提高自动化测试脚本的可维护性,此模式已经广泛应用于自动化测试领域。
使用面向对象的设计模式,页面对象模型将测试代码和被测试页面的页面元素及其操作方法进行分离,以此降低页面元素变化对测试代码的影响。每一个被测试页面都会被单独定义为一个类,类中会定位所有需进行测试操作的页面元素对象,并且定义操作每一个页面元素对象的方法。
例如,登录页面包括一个用户输入框和一个密码输入框,还有一个登录按钮。
我们声明一个名为Login的类,并且通过定位表达式找到用户名和密码输入框,并赋予类中的成员变量,分别定义输入用户名的方法、输入密码的方法和单击登录按钮的方法。
测试代码要完成登录测试,只需要调用Login类中输入用户名的方法、输入密码的方法和单击登录按钮的方法即可完成一个登录操作。如果登录页面的用户输入框、密码输入框或者登录按钮发生了位置变化,我们只需要修改Login类中的相关定位表达式和操作方法就可以完成维护,测试逻辑的脚本甚至不需要改变。
如果用户没有使用此模式,那么将登录过程都用相同的代码进行实现。如果在测试过程中需要多次登录操作,那么只能粘贴相同的代码来简化编写工作。但是可怕的情况是一旦页面元素发生了一点点改变,那么测试人员需要人工去把所有涉及变化的逻辑一一修改,会在不同的测试代码中进行搜索和修改,这样不但大大增加了工作量,而且很容易出现修改错误的情况。使用了页面对象模式,只需要修改一下唯一的Login类,就完成了大部分的维护工作。
2.使用PageFactory类
(1)使用PageFactory类给测试类提供待操作的页面元素
首先在src中新建一个package,名字为pageobjects,在下面新建一个页面对象类LoginPage。新建一个package,名字为testScripts,在下面新建一个测试类Test126mail。
LoginPage类的源代码如下:
Test126mail类的源代码如下:
从上面的实例中我们可以看到,页面元素的定位均在LoginPage类中实现了。如果页面元素发生了一定程度的调整,测试人员只需要修改LoginPage类中的定位表达式就可以完成基本的维护工作,测试类代码无需进行调整,从而降低了测试代码的维护工作。
(2)使用PageFactory类封装页面元素的操作方法
上面我们只为测试类提供了页面元素来进行操作,并没有在页面对象类中实现页面元素的操作方法。
LoginPage类的源代码如下:
Test126mail的源代码如下:
在页面对象中封装了页面元素的操作方法,使得在测试代码中实现测试逻辑更加容易。这些封装方法可以被很多测试逻辑重复调用,从而提高了代码编写和维护的效率,实现了一个类维护,很多测试类可被调用的目的,进一步降低了测试代码的维护成本。
(3)使用LoadableComponent类
继承LoadableComponent类可以在页面加载的时候判断是否加载了正确的页面,只需要重写isLoaded和load两个方法。此方式有助于让页面对象的页面访问操作更加健壮。
LoginPage类的源代码:
Test126mail类的源代码:
(4)多个PageObject的自动化测试实例
本节主要讲解多个PageObject的使用方法,以及如何基于多个PageObject实现一个相对复杂的自动化测试实例。
自动化测试实现的3个测试用例如下:
a.在126邮箱,使用正确的用户名和错误的密码进行登录,登录失败并在页面显示“帐号或密码错误”关键字。
b.在126邮箱,使用正确的用户名和正确的密码进行登录,登录成功后会跳转到邮箱文件夹列表首页,并且显示出“收件箱”关键字。
c.在126邮箱,登录成功后,单击“写信”链接,给testYY2017@126.com发送一封邮件,邮件发送成功后页面显示“发送成功”关键字。
LoginPage类:
HomePage类:
SendSuccessPage类:
Test126mail类
3.设计原则
(1)在PageObject类中定义public方法来对外提供服务。
(2)不要暴露PageObject类中的内部逻辑。
(3)不要在PageObject类中进行断言操作。
(4)只需要在PageObject类中定义需要操作的元素和操作方法。
(5)PageObject页面中的相同动作如果会产生多个不同的结果,需要在PageObject类中定义多个操作方法。