背景

广告 sdk 是什么

功能测试背景

自动化测试目的

基本的测试场景

下图展示了 sdk 功能测试的基本场景:

最常见的测试场景步骤:

测试难点

虽然测试场景和测试步骤看似简单,但是功能测试依然面临如下几个难点:

自动化测试方案需要满足的需求

依据基本的测试场景和功能测试难点,广告 sdk 自动化测试需要满足如下需求:

方案框架示意图

整个自动化测试框架,主要分为测试 demo、mock 服务器和控制服务器三部分:

分层设计

对整体架构进行了分层设计,主要分为框架层,逻辑层和数据层。

  通过对架构进行分层设计,将框架、逻辑和数据做分离,使得架构的健壮性和可复用性更强。自动化测试面临的一个问题是,方案的可复用性是否足以应对业务逻辑的变更。在我们的业务中,即使服务端与 sdk 之间的交互协议发生了变化,只需要对数据层的数据做变更即可。而不需要对逻辑和架构做修改。

控制服务器

  控制服务器是整个自动化测试框架的大脑,控制 case 执行,协同测试 demo 和 mock 服务器工作,并且对测试结果数据进行校验。说到自动化测试,大家可能有疑问,如何书写测试 case 并对 case 进行管理,如何控制 case 执行?如何控制测试 demo 自动完成广告请求、曝光、点击等操作?答案就是两个测试框架:cucumberappium

cucumber

Cucumber 是一个能够理解用自然语言描述的测试用例的支持行为驱动开发(BDD)的自动化测试工具,用 Ruby 编写,支持 Java 和·Net 等多种开发语言。 ---百度百科

cucumber 有两个特性:

下面详细了解一下控制服务器的数据层和逻辑层如何进行实现。

  我们将测试场景,抽象成一个个测试的执行步骤,然后用自然语言描述成 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,测试场景和校验逻辑一目了然。

  虽然 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 调用方式进行。该方案有如下特点:

UI 自动化

  UI 自动化通过操作测试 demo 完成对 sdk 的广告行为,该方案相当于模拟用户的操作行为,最接近真实的测试场景。该方案有如下特点:

  考虑到 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 自动化方案实现,面临如下问题:

考虑解决方案如下:

控制服务器框架图

要点:

mock 服务器方案

anyproxy

mock 服务器主要采用阿里开源的代理服务器 anyproxy 进行实现。anyproxy 主要有如下特点:

数据层

  前面提到控制服务器 cucumber 需要给 mock 服务器下发 mock 文件,对 mock 服务器的 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 打点数据与测试场景进行关联。

下面通过 “原生广告请求打点校验” 测试 case 为例,说明动态 mock 如何生效:

结果校验

  自动化测试的最后需要对测试数据进行校验,得出测试结论。前面已经提到,mock 服务器会将捕获的 sdk 请求和打点数据,并且写入数据库。为了达到校验准确性,需要进行如下三个方面校验:

  在我们的方案中,主要采用 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 小时/人次。自动化测试方案提升了测试效率,解放了人力。并且通过严格的打点数据校验,避免了人工疏漏造成线上问题。


↙↙↙阅读原文可查看相关链接,并与作者交流