查看原文
其他

广告sdk自动化测试实践

喻胜 360质量效能 2022-12-14

好久不见

团队小鲜肉最新实践分享~~

有木有很期待(*❦ω❦)



背景

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








您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存