2.4 自动化测试的设计模式
开发是有设计模式的,《设计模式:可复用面向对象软件的基础》一书中收录了23种经典的设计模式,但是这23种设计模式都是对开发的经典模式的总结,那自动化测试呢?其实自动化测试也是有设计模式的,典型代表就是PageObject模式(简称PO模式)。后续在PageObject模式的基础之上人们结合BDD的实践方法又推出了ScreenPlay模式,但是没有PageObject模式那么流行。
2.4.1 自动化测试的PageObject设计模式
PageObject设计模式是目前界面自动化中普遍应用的一种设计模式,其广为流传主要是因为它可以让自动化测试代码更容易维护,简化测试代码。PageObject设计模式的含义就是一个Page(页面)就是代码中的一个Object(对象),所以在应用PageObject设计模式进行测试自动化开发时,团队会为每个页面设计一个对象,而团队写业务代码时,由业务测试代码调用对象完成测试业务的逻辑代码。
下面以异步社区课程模块的访问流程为例讲解PageObject设计模式的好处。假设团队要测试异步社区课程端的以下两个流程。
选课流程:登录异步社区首页,进入课程页,选择对应的课程,进入课程详情页。
学习流程:登录异步社区首页,进入我的课程,选择要学习的课程,进入课程详情页,单击对应章节,进入课程播放页。
首先,要为异步社区上述流程中操作的每一个页面创建一个对应的类,页面-对象映射关系如图2-4所示。LoginPage就是登录页的类,IndexPage就是学习页的类,以此类推,这样就保障每一个页面都有一个对应类。
图2-4 页面-对象映射关系
在选课流程中要操作登录页、选课页、课程详情页,就需要操作登录页、选课页、课程详情页的代码类,完成对应页面的业务流串联。选课流程的PageObject设计模式如图2-5所示。
图2-5 选课流程的PageObject设计模式
在上述设计基础之上,如果要完成学习流程的业务测试脚本,就需要调用登录页、学习页、课程详情页及课程播放页。学习流程的PageObject模式如图2-6所示。
图2-6 学习流程的PageObject模式
通过以上示例,可以看出测试业务逻辑脚本和PageObject模式中的Object类产生了调用关系,和实际页面没有产生代码映射关系,所以这几个页面的代码仅仅写了一次。因此,PageObject模式降低了代码的冗余程度,提高了代码的可读性和可维护性。
同时,如果UI页面由于一些状况发生了一些变化(变化的内容影响了自动化代码,但是不影响业务),就无须将每一个应用到对应页面脚本的代码都修改一遍,只需要修改对应的Object代码即可。这既提高了修改效率,也降低了修改出错率。
PageObject设计模式在业务测试代码和被测试系统页面之间加了一层防护层,这样就可以将一些页面变更导致测试脚本必须修改的问题从这一层统一排除了。
PageObject设计模式的优越性就不言而喻,使用PageObject设计模式设计自动化测试明确分割了业务逻辑测试脚本和页面元素查找代码。
PageObject模式的UI测试框架中,绝大部分是通过抽象工厂模式设计PageObject类的。抽象工厂模式提供了一个创建一系列相关或相互依赖对象的接口,而无须指定具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式中的一种。
在抽象工厂模式中,工厂方法具有唯一性。在很多情况下,一个具体的工厂有一个工厂方法或者一组经过重载的工厂方法。
工厂却只能提供单个产品对象,因此才提出了抽象工厂模式。抽象工厂模式面对多个产品层级结构(产品层级结构即产品的继承结构,如果一个抽象类表示书,其子类表示计算机图书、绘本、小说,则抽象图书与具体品类的图书之间就构成了一个产品层级结构,抽象图书对应父类,而具体品类的图书对应其子类),一个工厂层级结构可以负责多个不同产品层级结构中产品对象的创建。
当我们可以根据一个工厂层级结构创建属于不同产品层级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更简单、高效。因此,从某种程度上说,抽象工厂模式就是简化的工厂模式。在Python中使用依赖、继承产生新的对象。下面以打印纸为例,建立一个表示打印纸的工厂类,如图2-7所示。
图2-7 建立抽象工厂类
实现代码如代码清单2-5所示。
代码清单2-5
GitHub中有很多PageObject模式的UI自动化测试框架的封装,但是本书并没有使用其原始示例,而基于开源项目page-objects,做了一些修改,且已经将其全部托管在GitHub上。本章开始部分就对PageObject模式下了定义,PageObject模式既涉及Page也涉及Object,包含里面的元素及对应的操作。因此,在page-objects项目中,除做一定的弥补之外,还在基础类库中添加元素的操作函数,并命名为hi_po。
首先,介绍对page-objects的改造。hi_po是基于Python语言的测试框架,结合WebDriver完成页面交互驱动,引入HTMLTestRunner来生成测试报告,引入Excel来进行数据驱动。然后,对应用的page-objects开源项目的代码进行分析。打开page-objects,发现这个项目的关键代码全部在__init__.py中,如代码清单2-6所示。
代码清单2-6
在page-objects中,先设计查找页面元素方法的枚举,以方便后续在任意一个页面的PageObject类中使用。在page-objects中还定义一个PageObject类,在PageObject类中调用WebDriver,如代码清单2-7所示。
代码清单2-7
PageElement类和MultiPageElement类用于处理页面的元素,两者分别处理返回一个元素和一组具有相同locator的元素。
对于PageElement,通过locater定位,返回一个WebElement类型的实例,通过该实例直接调用全部WebDriver的WebElements的API对页面的元素进行操作。
但page-objects并没有对一些常用的WebDriver实例的操作进行封装,并且对一些类似于下拉列表框、单选按钮的互斥元素或者相关元素也未进行处理和封装。针对上述问题,下面进入hi_po的设计环节。
定义GroupPageElement类,用于获取一组相关的页面元素,如一个下拉列表框、一组单选按钮等。该类放在page_objects的__init__.py文件中,目前GroupPageElement仅支持基于XPath查找,如代码清单2-8所示。
代码清单2-8
通过GroupPageElement类,显示一组页面元素。例如,在页面上显示一个下拉列表框,其代码如代码清单2-9所示。
代码清单2-9