本文介绍的是页面对象设计模式及其常见的滥用继承的错误。
本文和语言无关,但作者主要使用python和java。本文假设读者已经具有了一定的python或java基础,知道类和方法是什么。
如果完全没有这方面的基础,请看我的《测试人员如何学Python》。
页面对象模式主要用于基于图形界面的自动化测试,如果没有这方面的基础,请看我的《Selenium的学习》。当然,本文也适用于移动端的图形界面自动化。
一、页面对象模式简介
在使用页面对象模式之前的脚本,可能是这样的:
这样的脚本的问题就是,我如果创建了100个脚本,每个脚本里都有用户登录。
当有一天用户登录的页面改了:以前id叫username的文本框改成了id叫user。
我就不得不更新这全部的100个脚本。而网站往往隔三差五就要改版。这一百个脚本的维护工作很快就会把测试人员压垮。
于是,我们引入了页面对象,
所谓页面对象,即是页面元素加页面服务:
在引入页面对象之后,代码的组织是这样的:
具体到页面类内部,是这样的:
于是你看到的页面类这样的,此处我使用伪代码来描述,请自行转化成自己使用的语言。这里我是假设有一个在线电影网站(我的第一个自动化框架就是针对一个在线电影网站),提供用户注册功能。
而脚本则是这样的
这里脚本的脚本本身只有一行代码:
从注册页开始,先注册新用户,然后返回首页,然后搜索一部电影“雷神”,最后购买。注意,这仅仅是一个例子,我以方法链的形式来写了这个脚本,而且没有加断言。实际工作中,不建议你以方法链的方式来写脚本,实际上的脚本可能是写成这样的:
看完这个解释,大家是否对页面对象有了一定的理解了呢?
那么请尝试一下,找一个网站,给他的某个页面建立一个XXXPage类,然后封装一些这个页面的元素和服务吧。
二、面向对象不仅仅是继承
提到面向对象,大家可能在脑海里马上联想到了父类和子类,多态,接口等等。这部分是面向对象的主要内容,但并非全部内容。我在日常工作中,常常见到一些编码人员(测试或开发)写的代码是这样的,一个类套一个类,一个继承一个,十七八个类这么继承下来,使后续的维护者一头雾水。有时候维护者想要修改一些东西,不知道该改哪个类。这就说明原作者的设计过头了。所谓的“设计过度”的历史遗留代码,是我在工作中遇到的一个很常见的实际问题。
继承可能是这样的:
这样由上往下,从父类到子类。子类具有父类的属性和方法,并且有自己独有的新的属性和方法。
假设现在有一个在线电影网站要做自动化(这个网站大多数页面都带头部导航栏,导航栏里包括了“电影”,“电视剧”,“综艺”等菜单链接。),该自动化脚本的作者想要用页面对象模式封装这些页面。由于这位测试人员学过继承,于是他在测试框架里写了这样的类:
这是最典型的错误:滥用继承。
错误的点在于带导航栏的页面和不带导航栏的页面,不应该划分为两个类。
设想以下场景:
如果有一天需求变了。该网站支持了一批从第三方接入的用户(这也是一个我遇到过的实际需求噢)。对于这批新用户,要新增一种新版的头部导航栏,有些链接到原有内容,有些链接到这批用户独享的内容。网站改版成新旧导航栏并存。对老用户只开放老版导航栏,对第三方接入的新用户只开放新版导航栏。而页面本体没有其他任何变化。那么按照这种设计就会变成这样:
然后问题就来了:
页面1、2、3和7、8、9除了导航栏不一样以外,内容是完全一样的。
而只有页面10是专属于新的用户类型才能访问的页面。
此时,页面123和789里完全一样的内容写了两遍。那么日常维护量就变成了两倍。为了解决这种问题,需要引入组合模式。
三、加入组合模式
所谓组合模式,是这样的:
引入了组合模式后,页面对象模式是这样的:
于是,在刚才的RegisterPage类里加入了导航栏的实例之后,这个页面的代码就是类似于这样的:
仅仅只增加了一行代码,但是这个页面就得到了导航栏类的所有功能。假设导航栏里提供了goto_xxx_page()这样的方法,那么在调用时,只要
register_page.nav.goto_xxx_page()
这样就能在registerpage类里调用到导航栏类里的方法。
这样,就通过组合的方式,实现了页面之间功能更好的抽象。回头看那个导航栏发生变化的需求,一开始如果用了组合模式,那这个就不需要重复定义页面789了。
最后我们记住设计的原则:
把页面里可重用的模块,看作一个又一个的小页面。在页面类里通过组合的方式,把这些小页面组合进来。而不是搞一个复杂的继承树,一层套一层。
最后: 为了回馈铁杆粉丝们,我给大家整理了完整的软件测试视频学习教程,朋友们如果需要可以自行免费领取 【保证100%免费】
全套资料获取方式:点击下方小卡片自行领取即可