一 背景
政采云的搜索服务是基于 Elasticsearch 的在线分布式搜索,为内部业务提供结构化和非结构化数据的多条件检索,支撑政采云PC端、APP端、小程序端的搜索能力。
搜索以中间件形式提供服务,由于无法感知外部业务在哪用、怎么用,导致搜索测试一直沿用人工梳理场景,接口测试覆盖场景的方式。可通过人工去做,一是效率不高,二是场景覆盖不全。所以搜索的质量工作一直被如下问题所困扰:
基于上述问题,实践了一套基于流量的质量保障方案。
目标:借助流量区分活跃和非活跃代码,针对活跃代码,使用场景计算,自动识别和构建场景用例。针对非活跃代码,通过人工覆盖,补齐场景用例。
接下来分别介绍:活跃代码保障策略,非活跃代码保障策略,自动回归策略。
核心思想:借鉴流量录制回放的思路,通过采集 Dubbo 调用日志,清洗出场景数据,针对场景数据进行计算,生成场景用例。策略包含 3 个节点:
外部应用通过 Dubbo 调用搜索,搜索会输出一条日志,日志内容包含入参等信息。
此节点会根据指定格式清洗 Dubbo 日志,生成场景数据。
此节点是整个策略中最核心的节点,主要包含 3 块功能:1)入参模板化;2)生成模板指纹;3)根据指纹对流量去重。接下来详解实现方式。
外部应用传给搜索的入参是一个 JSON(图1到图2),搜索会把 JSON 转化成 ES 的查询语句(图2到图3),只要捕获业务传入多少种入参(图2),就能统计出有多少种搜索场景。
入参模板化方式有2种 :
// 仅供说明,非真实业务字段
搜索条件:
{
"bussinessScope": "浙江",
"status": true,
"keywords": "打印机"
}
生成的模板:
{
"bussinessScope": "@",
"status": @,
"keywords": "@"
}
上述示例对 bussinessScope、status、keywords 三个字段进行限定查询,类似 MySQL 的 where bussinessScope='浙江' and status=true and keywords='打印机'。只要查询字段相同,无论值怎么变,对搜索来说都是一类查询。按字段方式生成模板,只需替换掉 value,保留 JSON 骨架即可。
// 仅供说明,非真实业务字段
如上图所示:查询已上架的黑色数据线,搜索条件:
{
"attribute": "颜色:黑色",
"status": true,
"keywords": "数据线"
}
生成的模板:
{
"attribute": "颜色:黑色",
"status": @,
"keywords": "@"
}
上述示例对 attribute、status、keywords 字段进行限定查询,还需根据 attribute 的值进行数据匹配。针对有业务含义的搜索条件,需要保留对应的 value 值。生成具有业务含义的 JSON 骨架。
模板生成后,会根据模板算 MD5,生成唯一指纹。为什么要生成唯一指纹?因为搜索日均调用百万+,里面包含了大量重复查询。为了快速识别出重复查询,本方案会对每一个模板生成指纹,只要指纹相同,就属于一类查询。
// 仅供说明,非真实业务字段
描述:查询已上架的打印机
搜索条件:
{
"status": true,
"keywords": "打印机"
}
生成的模板:
{
"status": @,
"keywords": "@"
}
模板指纹:D8AD32393C65D62C8658A9D699A8C190
采集到新流量,生成新指纹,新指纹与已有指纹进行匹配,若相同则跳过。通过指纹匹配,相同指纹的搜索会被归为一类(如下图示例)。这样既能快速发现新场景,又能识别出重复流量。
// 仅供说明,非真实业务字段
描述:查询已上架的打印机
搜索条件1:
{
"status": true,
"keywords": "打印机"
}
生成的模板1:
{
"status": @,
"keywords": "@"
}
模板指纹1:D8AD32393C65D62C8658A9D699A8C190
////////////////////////////////////////////
描述:查询已上架的三星手机
搜索条件2:
{
"status": true,
"keywords": "三星手机"
}
生成的模板2:
{
"status": @,
"keywords": "@"
}
模板指纹2:D8AD32393C65D62C8658A9D699A8C190
模板1和模板2生成的指纹是一致的,通过对每一个模板的指纹进行对比,就能识别出相同搜索条件。
基于搜索现有流量,通过场景计算,自动生成线上P1场景用例 618 条,P2场景用例 516 条,P3场景用例 58 条,P4场景用例 161 条,总计 1353 条。
流量覆盖不到的代码是非活跃代码,保障策略采用人工覆盖。搜索的非活跃代码主要有:1)开关;2)异常场景;3)未使用的方法。分别通过如下方式保障:
通过流量和人工方式构建了场景用例,就得让用例产生价值。产生价值的方式是让用例自动“活”起来。搜索自动回归的流程如下,重点在:「预期结果池」和「校验规则」的建设。
目的:同一查询条件,一定命中相同预期结果
优化前:固定关键字即时搜索。
优化后:测试用例首次执行的结果,自动复制到预期结果池,非首次执行将查询预期结果池。
原因1:线上同一个搜索条件,间隔一段时间后再次搜索,存在返回结果不相同的情况。造成该情况的原因:用户操作或定时任务导致状态变更。如:A条件,第一次搜索返回 3 个商品 A、B、C,一段时间后,商品 B 下架。相同条件二次搜索返回 A、C、D。动态的预期结果不能很好的做校验,为了消除变动带来的影响,所以建立预期结果池。
原因2:线上索引数据 1亿+,若每条用例执行都扫 1亿+的数据,易产生慢SQL。预期结果池是独立索引,数据量几十万,相比查线上索引,速度快且不易产生慢SQL。
新增数据:用例库新增一条场景用例,首次执行会查询线上索引,同时把搜索结果复制到预期结果池。当用例第二次查询,自动路由到预期结果池。失效数据:逻辑变更测试通过,删除原有数据,重新走新增数据流程。
目的:根据使用场景,建立不同的校验规则。 优化前:自动化脚本里硬编码校验点。优化后:脚本与用例、校验规则解耦,根据使用场景,建立不同的校验规则。
当前自动化落地在2个场景:接口测试、重构测试,分别建立了2套校验规则。
校验重点:准确性。分为:总数准确和字段准确。总数准确:结合预期结果池,对比预期总数与实际总数。用例库新增一条场景用例,首次执行会查询线上索引,记录搜索结果总数(预期结果),同时把搜索结果复制到预期结果池。当用例第二次执行,自动路由到预期结果池。若二次搜索结果总数不一致,说明代码逻辑存在问题。字段准确:按字段维度进行校验。比如:校验区域字段,校验内容:「区域=浙江」。会校验每一个返回结果的区域字段是否等于浙江,或者包含浙江。
校验重点:全量对比返回结果和结果顺序。即同一搜索条件,新老接口返回值和返回顺序必须强一致。结果:沉淀2套场景校验规则,总计 37 个。其中接口测试规则 36 个(8个P1场景规则,19个P2场景规则,8个P3场景规则,1个P4场景规则),重构测试规则1个。
23年搜索代码将整体重构,要求测试人员对全场景进行保障。上半年分别进行了2次重构工作,内容如下:
该Bug发现于搜索重构项目,使用重构校验规则(全量对比返回结果,以及结果顺序)。
Bug描述:搜索结果顺序不一致,导致对比失败
Bug根因:老索引的id字段是 long 类型(左图),新索引的id字段是 keyword 类型(右图),字段类型变更,引发字段排序变化
易漏测点:在海量数据对比时,容易忽视顺序变化引发的问题,继而发生漏测。因为新老接口都返回相同数据,仅商品位置发生了变化。业务影响:上游业务搜索出商品,并按商品id排序,然后放到定时任务里执行。当商品位置变化,会造成已执行过的商品被重复执行,导致任务报错。