广告sdk自动化测试实践
好久不见
团队小鲜肉最新实践分享~~
有木有很期待(*❦ω❦)
背景
广告sdk是什么
广告sdk,是为接入移动广告平台的开发者提供的应用程序接口api的程序集合。广告sdk支持如下功能:
广告请求
广告打点
广告渲染
第三方app可以嵌入广告sdk,并且调用sdk的接口完成广告请求、广告展示、广告渲染、广告打点等逻辑。应用开发者可以关注自己的应用开发,而与广告相关的逻辑均交给广告sdk进行处理。
功能测试背景
近半年来,广告sdk提测频繁,版本迭代快,导致测试排期紧张,功能测试同事压力大。
广告sdk接入了多家广告平台,并且每一家广告平台都拥有十多种不同格式的协议,使得广告打点数据校验逻辑异常复杂。通过人工检查难以避免校验疏漏。
大量的广告打点数据校验导致回归测试工作量大。
自动化测试目的
通过自动化测试方案,提升测试效率,解放人力
通过代码的严格检查保证打点数据的正确性
形成sdk自动化测试的解决方案,为其他sdk业务测试团队提供借鉴思路
基本的测试场景
下面看一下sdk功能测试的基本场景:
最常见的测试场景步骤:
使用嵌入了sdk的测试demo,请求一次广告。sdk是jar包形式,sdk运行需要Android上下文支持,所以需要嵌入测试demo进行测试。
使用fiddler抓取sdk广告请求,并且校验广告请求数据是否正确。广告请求数据异常会导致线上无法返回广告。例如,某个版本在测试环境中因为某个字段的类型不正确,导致测试环境中无法返回广告。
依据测试case 在fiddler中mock广告数据返回给sdk。为什么需要mock服务端的广告数据呢?因为广告sdk针对不同类型的广告数据,会有不同的处理逻辑。例如,对于下载类广告和原生广告,同样是点击操作,而触发的广告行为完全不一致。所以功能测试过程中需要mock大量的不同类型的广告数据,来覆盖到sdk不同的功能逻辑,保障sdk的质量。
展示sdk获取到的广告。对广告进行展示,覆盖用户场景。
校验广告展示打点。打点数据涉及计费结算,数据正确性需要着重保障。
依次进行重复曝光、点击广告、下载等场景测试。
测试难点
虽然测试场景和测试步骤看似简单,但是功能测试依然面临如下几个难点:
打点数据多样性
广告sdk接入了多家广告平台,每个广告平台有10多种的广告协议。广告协议中携带的json数据中包含了30-40个数据字段。功能测试同事需要掌握大量的协议格式,才能保证打点数据的正确性。多样性的广告协议,给功能测试同事增加了非常大的工作量。自动化测试也同样面临打点数据类别区分以及正确性校验的难题。
打点时序控制
从前面的测试场景,我们知道各种类型的打点数据触发时机不一样,并且有严格的先后顺序。功能测试过程中,为了覆盖到所有的打点数据校验,测试同学需要设计各种操作场景来触发sdk打点行为。广告数据打点是在移动端进行触发,而抓包和mock在pc端进行。如何对两者进行协同,对打点时序的进行严格控制,也同样是自动化测试的难点。
mock数据频繁
服务端返回的广告数据直接影响sdk广告行为和打点数据。为了提高测试覆盖度,测试过程中往往需要构造各种类型的广告数据,达到覆盖各种测试场景的目的。自动化测试过程中,如何将设计的测试场景和mock数据进行关联,并且进行动态的变换同样是难点之所在。
自动化测试方案需要满足的需求
依据基本的测试场景和功能测试难点,广告sdk自动化测试需要满足如下需求:
自动触发广告行为
包括广告请求、曝光、点击等行为,还包括清理缓存,杀进程等行为
只有自动触发广告行为,才能让自动化测试真正脱离人工干预
动态mock
需要依据设计的自动化case,动态的mock不同的广告数据,覆盖各种测试场景和sdk的广告处理逻辑
数据校验
自动化测试最重要的一个环节,需要依据期望结果,校验实际结果,从而判定该条case是否通过。从而发现sdk存在的缺陷。
方案框架示意图
整个自动化测试框架,主要分为测试demo、mock服务器和控制服务器三部分:
测试demo(sdk宿主app)
测试demo嵌入了广告sdk,是测试的主体程序。
mock服务器
同时也是代理服务器
抓取广告请求数据、打点数据
为sdk mock广告数据
控制服务器
case管理
控制case执行
协同sdk与mock服务器进行场景测试
进行结果校验
分层设计
对整体架构进行了分层设计,主要分为框架层,逻辑层和数据层:
框架层,主要采用开源或是第三方框架作为底层计算、调度支持。
逻辑层,主要依据底层框架开放的接口,实现符合自身业务需求的代码逻辑。例如,控制服务器通过逻辑层对具体的测试场景进行实现。
数据层,主要是测试过程需要用到的一些数据,包括测试case、mock的广告数据等。
通过对架构进行分层设计,将框架、逻辑和数据做分离,使得架构的健壮性和可复用性更强。自动化测试面临的一个问题是,方案的可复用性是否足以应对业务逻辑的变更。在我们的业务中,即使服务端与sdk之间的交互协议发生了变化,只需要对数据层的数据做变更即可。而不需要对逻辑和架构做修改。
控制服务器
控制服务器是整个自动化测试框架的大脑,控制case执行,协同测试demo和mock服务器工作,并且对测试结果数据进行校验。说到自动化测试,大家可能有疑问,如何书写测试case并对case进行管理,如何控制case执行?如何控制测试demo自动完成广告请求、曝光、点击等操作?答案就是两个测试框架:cucumber和appium。
cucumber
Cucumber 是一个能够理解用自然语言描述的测试用例的支持行为驱动开发(BDD)的自动化测试工具,用Ruby编写,支持Java和·Net等多种开发语言。 ---百度百科
cucumber有两个特性:
理解自然语言描述的测试用例
支持行为驱动开发(BDD)
下面详细了解一下控制服务器的数据层和逻辑层如何进行实现。
case描述(数据层)
我们将测试场景,抽象成一个个测试的执行步骤,然后用自然语言描述成cucumber支持的格式。最后cucumber通过BDD框架控制case执行。 自动化测试case实例:
场景:MAX原生广告"请求"及"请求打点"完整性校验
假设 caseID"10001"
假设 初始化mock文件为"360原生广告"
假设 打开原生广告界面
并且 填写广告条数"3"
并且 填写广告位条数"1"
当 点击按钮"获取广告"
那么 请求的完整性如"juhe-adrequest-schema"描述那么 请求及字段值校验如下
|count| url | field | value |
| 1 |https://domain?pver=2| adtype | 3 |
| 1 |https://domain?pver=2| adspaceid | 5a56tq |
那么 请求的完整性如"juhe-tk-request-schema"描述
并且 聚合请求打点字段校验
|count |type | field | value |
| 1 | 0 |agspaceid | ag56q0xf |
| 1 | 0 |adspaceid | 5a5q08xf |
| 1 | 0 |agappkey | ag318422 |
| 1 | 0 |adappkey | 318422 |
| 1 | 0 |plid | 1 |
| 1 | 0 |channelid | 7 |
| 1 | 0 |status | 0 |
| 1 | 0 |reqnum | 3 |
| 1 | 0 |resnum | 1 |
上述的自动化测试case描述了对广告请求字段进行校验的测试场景。测试case采用自然语言对测试场景进行描述,包含测试过程中前置条件、测试步骤和测试结果校验等信息。通过cucumber支持的假设、当、并且、那么等关键字对测试case进行定义。通过该种方式整理case,测试场景和校验逻辑一目了然。
case执行(逻辑层)
虽然cucumber支持的BDD框架控制case执行,但是需要我们自己对测试case描述的每个步骤逻辑进行实现。这些步骤中,需要实现case描述的具体逻辑。 例如,case中描述的步骤“点击按钮”,步骤实现逻辑需要调用appium相关接口对测试demo按钮进行点击。
@假设("^caseID\"([^\"]*)\"$")
public void getCaseID(String arg0) throws Throwable {
// Write code here that turns the phrase above intoconcrete actions
// throw new cucumber.api.PendingException();
}
@假设("^打开原生广告界面$")
public void openNativeActivity() throws IOException {
}
@并且("^填写广告条数\"([^\"]*)\"$")
public void fillInAdNum(String adnum) throws IOException{
}
@当("^点击按钮\"([^\"]*)\"$")
public void clickButton(String btnType){
}
@那么("^请求及字段值校验如下$")
public void checkUrlField(Listlist){
}
demo自动化方案
方案选型
通过demo对广告sdk进行自动操作,是整个自动化测试最为关键的一个环节。只有测试demo可以“自主”请求广告、曝光、点击才能完全进行自动化测试。如果sdk不能自主触发广告行为,会直接导致测试case执行失败。在考虑demo自动化测试方案时,面临两种选择:接口调用方式以及UI自动化。
接口调用
广告sdk的广告行为,都是通过宿主app调用sdk的各种api进行实现的。例如,宿主app的广告请求行为,也就是调用sdk的广告请求api完成。所以demo自动化方案,也可以通过api调用方式进行。该方案有如下特点:
直接调用sdk接口
实现简单
传递参数复杂(需要特定的语义描述复杂的测试场景,各种接口调用顺序,以及各个接口的参数)
不支持清空缓存、下载第三方app等操作(sdk需要对一些复杂的操作场景进行测试)
UI自动化
UI自动化通过操作测试demo完成对sdk的广告行为,该方案相当于模拟用户的操作行为,最接近真实的测试场景。该方案有如下特点:
需要第三方UI自动化框架支持
模拟用户操作,符合实际操作场景
支持复杂的操作场景(杀进程、清空缓存等)
需要进行机型适配(权限弹窗点击、安装弹窗等)
考虑到sdk测试,很多时候需要对测试demo进行杀进程、清理缓存、安装第三方app等操作,故最终选择UI自动化测试实现demo自动化方案。
UI框架选型
前面提到了,我们为了覆盖复杂的测试场景选择UI自动化方案对广告sdk进行自动化操作。考虑到广告sdk有Android和ios两个版本,需要ui自动化框架具有跨平台特性。我们采用appium测试框架实现测试demo的自动化操作。
Appium是一个开源的、跨平台的自动化测试工具。它适用于原生应用、混合应用和移动网页应用。该框架支持iOS,Android和FirefoxOS的模拟器和真机。
我们通过appium对demo进行自动化操作,从而完成各种测试场景的操作行为。
UI自动化方案要点
UI自动化方案的健壮性直接关系到自动化测试是否能够顺利完成。对测试demo进行自动化操作的过程中,如果出现获取控件元素失败等异常情况,则会导致该条case执行失败。UI自动化方案实现,面临如下问题:
权限弹窗问题
广告sdk有一些测试场景,需要清理缓存,然后重新启动。这个时候在某些品牌手机上会有权限弹窗提示,影响测试demo按钮点击,从而影响case顺利执行。
安装弹窗问题
广告sdk的下载类广告,需要下载第三方app并且安装。安装过程中会有安装界面调起,这时候覆盖测试demo影响测试正常进行。
考虑解决方案如下:
轮询(等待)+重试机制
针对第三方app下载时间不确定性,可能导致安装界面覆盖测试demo。我们考虑的方案是,等待app下载完成,并且安装完成再进行demo上的下一步操作。
对某些控件的点击增加重试机制,保障测试场景顺利进行。
需要对轮询机制进行条件限制(轮询次数或者轮询时间进行判断)避免陷入死循环。
兼容性考虑
考虑多条件组合查询,利用xpath、resourceid或text对控件元素进行定位。
权限弹窗上的按钮在不同的手机上可能有不同的resource-id,这个时候可能需要借助xpath方式进行元素定位,甚至需要text进行辅助。
执行步骤之间的停顿
由于不同手机的性能不一样,处理ui操作的速度也不一样。很多时候cucumber控制步骤之间,需要一些停顿时间才能保证下一步骤执行前,上一个步骤已经执行完毕。
rerun机制
即使对兼容性方案进行了充分考虑,还是存在意外情况导致ui操作执行失败,导致该条测试case测试不通过。此时,通过rerun机制排除意外因素对测试结果干扰。
效率和准确性权衡
为了让ui自动化方案可以适配更多手机,可能增加更多的判断逻辑,这样兼容性会更强,但同时也会影响代码执行效率。
元素定位不同的定位方式也有区别:xpath定位控件效率低,但兼容性更强,定位元素准确。而resource-id定位控件快速,但是兼容性较弱。
在实际项目中,我们需要依据实际项目情况对兼容性和效率进行权衡。如果需要适配大量的不同机型的手机,那么需要对兼容性考虑更全面一些。
控制服务器框架图
要点:
cucumber是广告sdk自动化测试的控制框架。
cucumber通过mock配置文件告知mock服务器(anyproxy),需要抓取什么样的数据包,以及需要如何mock数据。
cucumber通过appium框架提供的api,完成对demo的自动操作。
cucumber需要从数据库中获取结果数据,并对结果数据进行校验,得出测试结论。
mock服务器方案
anyproxy
mock服务器主要采用阿里开源的代理服务器anyproxy进行实现。anyproxy主要有如下特点:
基于nodejs
提供web版界面,可以实时观测网络请求
开放接口,允许用户自定义规则,易于二次开发
数据层
前面提到控制服务器cucumber需要给mock服务器下发mock文件,对mock服务器的mock逻辑进行控制。主要涉及以下两个文件:
mock配置文件
json形式,以key-value形式指定mock规则。json中key字段为需要抓包或mock的url,value是相应的mock数据文件地址。
指定抓包的url:由于在测试过程中,代理服务器捕获的网络请求不只有sdk发送的请求,也有pc端和手机端其他的数据流量。为了避免这些数据影响测试结果的校验,所以在mock服务器端指定需要捕获和mock的网络请求。
mock的数据文件
不同的测试场景,需要mock不同的广告数据,覆盖sdk不同的代码逻辑。针对每条测试case,都需要指定该测试场景对应的mock数据。
逻辑层
虽然数据层规定了mock规则以及给定了mock数据。但是还是需要在逻辑层对具体的mock逻辑进行实现。而anyproxy提供一系列接口供我们方便实现抓包和mock逻辑。mock服务器在每一个case执行结束,都需要将抓取的广告打点数据存入数据库,供控制服务器进行结果校验。
mock服务器数据交互图,如下:
动态mock实现
前面已经提到自动化测试方案需要满足动态mock的测试需求。我们通过mock配置文件+控制信号实现了动态mock。 mock服务器的mock规则是通过mock配置文件进行控制的。mock配置文件指定了mock服务器需要抓取的数据包和对应的mock数据。而通过控制型号,控制服务器会依据测试场景动态更改mock配置文件。通过控制信号和mock配置文件的协同,实现了动态mock。
数据流图
为了保证每条测试case顺利执行,控制服务器通过控制协议协同测试demo、mock服务器一起工作。 控制协议数据流图如下所示:
包括三种控制协议:
每种控制信号都携带caseID,将sdk打点数据与测试场景进行关联。
测试开始信号:
控制服务器通知测试demo和mock服务器新的一条测试case开始执行。
mock服务器接收到测试开始信号,需要重新从服务器获取mock配置文件和mock数据文件。接下来依据mock配置文件的规则进行抓包和mock。从而满足该条测试case的测试需求。
测试结束信号
控制服务器通知测试demo和mock服务器该条case执行结束。
mock服务器接收该信号后,将抓到的数据包写入控制服务器数据库。
控制服务器从数据库读取测试数据,并对测试数据进行校验,输出测试结果。
mock文件更改信号
mock服务器接收到该信号后,重新从控制服务器端获取mock配置文件,更改mock规则。
下面通过“原生广告请求打点校验”测试case为例,说明动态mock如何生效:
控制服务器cucumber获取一条新的case,准备该条case对应的mock配置文件和mock数据文件。
控制服务器依据caseID,在数据库中创建新的数据项,用于存储sdk打点数据。
cucumber通过appium控制测试demo发送测试开始信号(控制信号)。
mock服务器收到测试开始信号后,从控制服务器获取该测试场景的mock配置文件和mock数据文件。
然后cucumber通过appium控制sdk执行一系列广告操作。
mock服务器依据mock配置文件的规则进行抓包和mock。
控制服务器通过appium控制测试demo发送测试结束信号,mock服务器收到该信号后,将捕获的打点数据写入控制服务器数据库。
在复杂的测试场景中,如果需要对mock规则和mock数据进行更换,那么控制服务器通过mock更改信号通知mock服务器,重新获取mock相关文件。
最后控制服务器对数据库中的打点数据进行结果校验。
结果校验
自动化测试的最后需要对测试数据进行校验,得出测试结论。前面已经提到,mock服务器会将捕获的sdk请求和打点数据,并且写入数据库。为了达到校验准确性,需要进行如下三个方面校验:
数据格式(json字段类型、字段是否有缺失等)
请求数量
关键字段值
在我们的方案中,主要采用json-schema协议对网络请求的json数据进行校验。json-schema是一种基于json格式定义的json数据结构规范。围绕jsonschema协议有丰富的开源实现。通过这些基于json-schema协议的开源实现可以实现json-schema自动生成,json数据验证。下面看一下具体的json-schema示例:
原始json
{
"id": 2,
"name": "An ice sculpture",
"price": 12.5
},
{
"id": 3,
"name": "A blue mouse",
"price": 25.5
}
生成的json-schema
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Product",
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"description": "The unique identifier for a product",
"type": "integer"
},
"name": {
"description": "Name of the product",
"type": "string"
},
"price": {
"type": "number",
"minimum": 0,
"exclusiveMinimum": true
}
},
"required": [
"id",
"name",
"price"
]
}
}
通过json-schema协议提供的语义,我们可以定义每个json字段类型,以及该字段的必要性等特性。与此同时,还可以对字段值进行一定的限定。int类型的数据,可以指定范围,也可以依据业务场景限定为特殊的值。string类型的值支持正则匹配。并且我们通过业务json模板文件可以自动生成校验数据需要的json-schema。不需要我们进行手动构建,节省了大量的工作量。
方案运行情况
目前已经梳理了75条自动化测试case,占到case总量的25%,预计将来可以达到40%。
目前自动化测试方案,已经部署到持续集成环境中,开发提测以及上线前都可以直接运行自动化测试方案对sdk进行自动化测试。自动化测试使得回归测试耗时从10小时/人次减少到0.5小时/人次。自动化测试方案提升了测试效率,解放了人力。并且通过严格的打点数据校验,避免了人工疏漏造成线上问题。