文章目录
互联网产品的测试策略设计
互联网产品测试策略与传统的不同
- 通常情况下,互联网产品要求全回归测试执行时间不能超过4个小时
- 在保证测试质量和测试覆盖率情况下,如何缩短测试执行时间
- 可以引入测试并发执行机制,用包含大量测试执行节点的测试执行集群来并发执行测试用例
- 常见的测试执行集群,由一个主节点(master)和若干子节点(Node)组成。主节点用来分发测试用例到各个子节点,各子节点用来具体执行测试用例
- 使用新的测试策略
传统软件产品的测试策略设计
单元测试(Unit Test)
- 属于白盒测试范畴,通常由开发工程师自己完成,能够及早发现问题
- 每次build过程中都会多次反复执行单元测试
- 测试用例会很多
API测试
- 主要针对各模块暴露的接口,通常采用灰盒测试方法。灰盒测试介于白盒和黑盒之间的一种技术,核心思想是利用测试执行的代码覆盖率来指导测试用例的设计
- 测试用例少于单元测试,多于GUI测试
GUI测试
- 称为端到端测试,是最接近软件真实用户使用行为的测试类型
- 模拟用户在软件界面上的各种操作,验证操作对应的结果是否正确
- 优点:直接验证商业价值
- 缺点:执行的代价较大,就算采用自动化技术,用例的维护和执行代价依然很大
互联网产品的测试策略设计
GUI测试
- 互联网的上线周期决定了GUI测试不可能大范围开展
- GUI测试通常采用"手工·为主、自动化为辅"的测试策略
- 手工测试往往利用探索性测试思想,针对新开发或者新修改的界面功能进行测试
- 自动化测试关注点主要放在相对稳定且核心业务的基本功能验证上
- 所以,GUI自动化测试一般只覆盖最核心且直接影响朱颖业务流程的E2E场景
API测试
- 互联网测试重点是API测试
- 原因:
- API测试用例开发与调试效率比GUI测试高很多
- API测试用例的执行稳定性远远高于GUI测试
- 单个API测试用例的执行时间往往比GUI要短
- 多采用为服务架构,针对微服务的测试,本质上是对不同的web service测试,也就是API测试
- API接口改动一般比较少,即使改动,绝大多数情况下也要保证向后兼容性(基本要求就是原本API调用方式维持不变)
- 互联网产品特性决定了,API测试可以实现良好的投入产出比
- 重量级API测试,轻量级GUI测试,轻量级单元测试
单元测试
- 互联网产品追求的是快速的功能实现并上线,不会有时间做全面的单元测试
- 这里单元测试策略要采用"分而治之"的思想
- 互联网产品通常分为应用层和后端服务,后端服务又可以进一步分为应用服务和基础服务
- 后端基础服务和一些公共应用服务相对稳定,对系统全局很重要,所以有必要展开全面的单元测试,对于变动非常频繁的客户端应用和非公用后端应用服务,一般很少做单元测试
- 对于一些核心算法和关键应用,如:银行网关接口,第三方支付集成接口,也要做比较全面的单元测试
GUI自动化测试
- 一个具体的例子,使用selenium请求百度
form selenium import webdriver
driver = webdriver.Chrome() # 创建实例
driver.get('http://www.baidu.com') # 打开页面
driver.findElement_BY_Id('wd').send_key('xx') # 获取标签,输入信息
driver.findElement_BY_Id('su').click() # 点击
time.sleep(3) # 停留时间
driver.close() # 关闭
Selenium实现原理
- selenium1.0的核心是基于selenium RC
- selenium2.0的核心是webdriver
- selenium3.0和2.0相比没有本质变化,主要增加了对MacOS的Safari和Windows的Edge支持,彻底删除了对Selenium RC的支持
selenium1.0的工作原理
- 又称selenium RC,RC是remote control的缩写,原理是JS代码可以很方便的获取页面上的任何元素并执行各种操作
- 由于’同源策略’,要想在测试用例中运行浏览器,注入js代码实现web自动化操作,selenium RC就必须欺骗被测站点,让他认为被注入的代码是同源的
- 显示欺骗,是引入selenium RC server的根本原因,利用Http Proxy模块来欺骗浏览器
- 除了selenium RC server, RC方案的另一大部分就是Client Libraries
selenium RC server
- 主要包括selenium core, http proxy和launcher三部分
- selenium core,是被注入页面中的js函数集合,用来实现界面元素的识别和操作
- http proxy,作为代理服务器修改的js源,达到欺骗站点的目的
- launcher,用来在启动测试浏览器时,完成selenium core注入和浏览器代理设置
client libraries
- 是测试用例代码向selenium RC server发送http请求的接口,支持多种语言
- 执行流程:
- 1,测试用例基于不同语言client libraries向selenium RC serverz发送请求
- 2,建立连接后,selenium Rc server中的Launcher会启动浏览器,把selenium core加载到浏览器页面当中,同时把浏览器代理设置为http proxy
- 3,测试用例通过client libraries向selenium Rc server发送Http请求,selenium RC server解析请求,通过http proxy发送js命令通知selenium core执行浏览器上控件的具体操作
- selenium core接收到指令后执行操作
- 如果浏览器接受到新的页面请求信息,则会发送http请求新的web页面,由于lanucher在启动浏览器时把http proxy设置为浏览器代理,所以selenium RC server会接收所有由他启动的浏览器发送的请求
- selenium RC server接收到浏览器发送的Http请求后,重组Http请求以规避’同源策略’,获取对应的web页面
- http proxy把接受的web页面返回给浏览器,浏览器对接受的页面进行渲染
selenium2.0工作原理
- 利用浏览器原声的webdriver实现页面操作
- selenium webdriver是典型的server-client模式,server端就是remote server
- 原理:
- 使用selenium2.0启动浏览器web browser时,后台会同时启动基于webdriver wire协议的web service作为selenium的remote server,并将其与浏览器绑定,绑定完成后,remote server就开始监听client端的操作请求
- 执行测试,测试用例会作为client端,将需要执行的页面操作请求以Http Request的方式发送给remote server,该Http Request的body是以webdriver wire协议规定的json格式来描述需要浏览器执行的具体操作
- remote server接收到请求后,会对请求进行解析,并将解析结果发送给webdriver,由webdriver实际执行浏览器操作
- webdriver可以看作直接操作浏览器原生组件(Native Component)
脚本与数据的解耦+PageObject模型
测试脚本和数据的解耦
- 未抽离存在大量重复代码,且维护成本太高
- 抽离后,测试脚本只有一份,在需要输入数据的地方使用变量替代,把测试输入数据单独放在一个文件中,通常是表格形式CSV文件
- 测试脚本通过读取数据,赋值给相应的变量,执行测试用例,读完所有数据测试结束
典型的数据驱动(data-driver)测试
- 解决了大量重复脚本问题,实现了’测试脚本和数据的解耦’。目前所有成熟的自动化框架都支持数据驱动测试,数据源除了CSV,还包括xls,json,yaml,QTP直接使用数据库表
- 数据文件中不仅可以包含测试输入数据,还可以包含测试结果数据,甚至可以包含测试逻辑分支的控制变量
- 数据驱动的思想不仅适用于GUI测试,还可以用于API测试,接口测试,单元测试等,很多API测试工具(如:SoapUI),以及单元测试框架都支持数据驱动测试
页面对象(Page Object)模型
- 早期的GUI自动化测试脚本,无论是开源的selenium开发,还是商用的QTP(Quick Test Professional,现在改名为Unified Functional Testing)开发,脚本通常是由一系列页面控件的顺序操作组成的
- 脚本的缺点主要在于:
- 脚本逻辑层次不够清晰,属于all-in-one风格,既有页面元素的定位查找,又有对元素的操作
- 脚本的可读性差
- 由于脚本的每一行都直接描述各个页面上的元素操作,很难一眼看出脚本更高层的业务测试流程
- 通用步骤会在大量测试脚本中重复出现
- 也就是开发几个GUI自动化测试会觉得高效,开发成百上千GUI自动化测试会很痛苦
- 解决方案:
- 利用软件设计中模块化设计思想,把一些通用操作打包称有确定含义的函数,使用GUI自动化脚本直接调用这些操作函数来构成整个测试用例
- 这样就从’流水账’过渡到了’可重用脚本阶段’
- 模块化思想带来的好处是
- 解决了脚本可读性差的问题,脚本的逻辑层次更清晰了
- 解决了通用步骤在大量测试脚本中重复出现的问题
- 不足
- 并没有解决早期GUI自动化测试的主要问题,如:每个操作函数内部脚本的可读性问题依然存在
- 引入了新的问题
- 如何把控操作函数的粒度,以及如何衔接两个操作函数之间的页面
如何解决早期GUI自动化测试’可读性差,难以维护’问题
- 操作函数内部还停留在’既有元素定位查找,又有元素操作’,当业务操作本身比较复杂或者需要跨多个页面时’可读性差,难以维护’问题就更明显
- 解决办法页面对象模型
- 核心理念:以页面(web page或者Native app page)为单位来封装页面上控件以及控件的部分操作,而测试用例(操作函数),基于页面封装对象来完成具体的界面操作,最典型的模型是’XXPage.YYComponent.ZZOperation’
GUI自动化测试中’业务流程business flow’概念、思想和场景
如何把控操作函数粒度
- 操作函数是指,一个操作函数到底包含多少操作步骤才是最合适的
- 如果粒度太大,会降低操作函数的可重用性
- 如果粒度太小,会失去操作函数封装的意义
- 操作函数粒度控制,很大程度1上取决于项目的实际情况,以及测试用例步骤的设计,没有绝对标准
- 脚本粒度的控制还是有设计依据遵循的,即往往完成一个业务流程(business flow)为主线,抽离出其中的’高内聚低耦合’操作步骤集合,操作函数就有这些步骤集合构成
- 如何衔接两个操作函数之间的页面
- 完成一个业务流程操作,需要调用多个操作函数,但是函数之间有页面衔接问题,即前序操作函数完成后的最后一个页面,必须是后续操作函数的第一个页面
- 如果无法衔接,那么就需要在两个操作哈桑农户之间加入额外的页面跳转代码,或者在操作函数内部加入特定的页面跳转代码
业务流程抽象
- 业务流程抽象是基于操作函数的更接近实际业务的更高层次的抽象方式,基于业务流程抽象实现的测试用例往往灵活性会非常好,可以非常方便的组装出各种测试用例
- 对于每一个业务流程,都会有一个相应的业务流程输入参数类与之一一对应,具体步骤
- 初始化一个业务流程输入参数类的实例
- 给这个实例赋值
- 用这个输入参数实例化来初始化业务流程类的实例
- 执行这个业务流程实例
- 执行业务流程实例的过程,其实就是调用操作函数来完成具体的页面对象操作的过程
业务流程的特点
- 业务流程的封装更接近实际业务
- 基于业务流程的测试用例非常标准化,遵循’参数准备’,‘实例化Flow’,和’执行Flow’这三大步骤,非常适用于测试代码的自动生成
- 由于更接近实际业务,所以可以很方便的和BDD结合,BDD就是Behavior Driven Development,行为驱动开发
- 业务流程特别适合于组装面向终端用户的端到端(E2E)系统功能测试用例,尤其适用于业务功能非常多,并且存在各种组合的E2E测试场景
GUI自动化过程中的测试数据
- 从创建的技术手段来说,创建测试数据方法主要分三种
- API调用
- 数据库操作
- 综合运用API调用和数据库操作
- 从创建的时机来讲,创建测试数据方法主要分为两种:
- 测试用例执行过程中,实时创建测试数据,称为On-the-fly
- 测试用例执行前,事先创建好’开箱即用’的测试数据,称为OUt-of-box
- 在实际项目中,对于创建数据的技术手段而言,最佳选择是利用API来创建数据,只有当API不能满足数据创建需求时,才会使用数据库操作的手段
- 实际中很多数据往往是基于两者结合完成,先通过API创建基本数据,然后调用数据库来修改数据,达到对测试数据的特定要求
- 而对于创建数据的时机,往往时两种结合用
- 对于相对稳定的测试数据,采用Out-of-box方式提高效率
- 对于那些只能使用一次性的测试数据,往往采用On-the-fly以保证不存在脏数据问题
基于API调用创建测试数据
- 如:用户注册,当用户通过GUI界面完成用户注册信息填写后,向系统后台递交表单,系统后台就会调用createUser的API完成用户创建
- 对于互联网产品,尤其是大量采用微服务架构的网站,这个API往往以web service形式暴露接口,在这种情况下,完全可以·1调用这个API来创建新用户,无须再向后台递交表单
- 由于API通常有安全相关的TOKEN机制来保护,实际项目中,通常会把对这些API的调用以代码形式封装作为测试数据工具
- 优点:数据的准确性由产品API直接负责,缺点:并不是所有数据都有相关API支持
- 对于大量创建数据来说,基于API调用方式的执行效率不理想,所以采用了基于数据库操作的创建手段
基于数据库操作创建测试数据
- 实际项目中,很多数据的创建和修改直接在产品代码内完成,并没有对外暴露供测试使用的接口
- 可以把创建和修改数据相关的SQL语句封装称测试数据工具,方便测试用例调用
- 为了不造成数据的遗漏,导致测试因数据问题无法进行‘
- 1,手工方式。查阅相关设计文档和产品代码,找到相关的SQL语句集合
- 2,自动方式,在测试环境中,先在只有一个用户情况下,操作数据,利用数据库监控工具获取这段时间内所有的业务表修改记录
- 两种思路的前提是,假定产品功能正确
- 缺点是:数据库表操作的任何变更,都必须同步更新到测试数据工具的SQL语句
- 在实际工作中,需要引入测试数据工具的版本管理,并通过开发流程来保证SQL变更能及时通知测试数据工具团队
综合运用API调用和数据库操作创建测试数据
- 如果需要创建一种特定的测试数据时,没有API支持,可以先通过API创建一个基本的数据,然后再通过修改数据库的方式来更新数据
实时创建数据:On-the-fly
- GUI测试脚本中,在开始执行界面操作之前,往往会通过调用测试数据工具实时创建测试数据,也就是On-the-fly方式
- 不依赖被测系统的任何原有数据,也不会对原有数据产生影响,可以很好的从数据层面隔离测试用例,让测试用例实现’自包含’
- 实际使用中,会存在三个问题:
- 在用例执行过程中实时创建数据,导致测试执行比较长
- 业务数据的连带关系,导致测试数据的创建效率非常低
- 实时创建测试数据方式对测试环境的依赖性很强
- 这时引用了Out-of-box
事先创建数据:Out-of-box
- 开箱即用,在被测系统中预先创建好了充足的、典型的测试数据。这些数据通常是在搭建测试环境时通过数据库脚本’预埋’在系统中地,后续地测试用例可以用直接使用
- out0of-box解决了on-the-fly一些问题,缺点也很明显:
- 测试用例中需要硬编码(hardcode)测试数据,额外引入了测试数据和用例之间地依赖
- 只能被一次性使用地测试数据不适合Out-of-box方式,如:优惠券使用一次就失效
- '预埋’地测试数据可靠性远不如实时创建的数据,测试用例执行过程中,经常会出现测试数据被修改的情况
On-the-fly和Out-of-box互补
- 基于两者的优缺点和互补性,在实际大型项目中,会采用两者结合的方式,从测试数据本身特点入手,选区不同的测试数据创建方式
- 1,针对相对稳定、很少有修改的数据,建议采用out-of-box方式(如:商品类目、厂商品牌、部分标准的卖家和买家账号)
- 2,对于一次性使用、经常需要修改、状态经常变化的数据,建议使用on-the-fly方式
- 3,用on-the-fly方式创建的测试数据,上游数据创建可以用采用out-of-box方式,以提高测试数据创建的效率(以订单数据为例,订单的创建可以采用on-the-fly方式,而与订单相关的卖家、买家和商品信息可以使用Out-of-box方式创建)
- 二者结合使用寻求数据稳定性和数据准备效率之间的最佳平衡