360Qtest专栏 广告 sdk 自动化测试实践

willys · 发布于 2017年11月09日 · 最后由 YangClyde 回复于 2017年11月16日 · 975 次阅读
本帖已被设为精华帖!

背景

广告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自动完成广告请求、曝光、点击等操作?答案就是两个测试框架:cucumberappium

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(List<UrlRequestField> list){

}

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数据结构规范。围绕json-schema协议有丰富的开源实现。通过这些基于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小时/人次。自动化测试方案提升了测试效率,解放了人力。并且通过严格的打点数据校验,避免了人工疏漏造成线上问题。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 21 条回复
3623 026 将本帖设为了精华贴 11月09日 21:49
3623 026 将本帖设为了精华贴 11月09日 21:49
605

很完整,今天朋友圈也转发了,点赞~

50

支持一下

1522fe

学习下,APP这块是我的弱项。

9260
605chenhengjie123 回复

哈哈哈,谢谢~~😊 😊

15969

赞!~

377b84

写的很棒,我去找找作者

8a13a1

很棒~👍

981500

不错的设计思路,然后 我现在才知道你想要确定json-schema的字段是必选或非必选😂 😂 ,一般都是在请求的时候会有说明是 必选还是非必选;我们这边的业务场景比较少 说明 response返回的数据要求 必选或非必选

9260
willys · #14 · 2017年11月10日 作者
981500hu_qingen 回复

哈哈哈

96

这个有开源吗

5169

非常完整的一套方案,特别是mock的实现,原来还可以这么用。学习了!点个赞!

9260
willys · #17 · 2017年11月12日 作者
32cool 回复

目前没有。并且有些部分代码与业务逻辑紧密结合。

56d552

@willys 写的很好,我想把这个帖子转到我们公司内网论坛,并注明出处,可以么?

10945

前年测广告SDK也想了很久的UI自动化~ 各种局限,今天涨姿势了。

9260
willys · #20 · 2017年11月14日 作者
56d552YangClyde 回复

可以的。

56d552
9260willys 回复

3Q~

9260
willys · #22 · 2017年11月14日 作者
56d552YangClyde 回复

请问贵司是??

56d552
9260willys 回复

洛阳艾克科技

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册