作者简介:
- 詹柏林,来自货拉拉/技术中心/质量保障部/国际化,资深测试工程师,负责移动端测试工作
- 余晓艳,来自货拉拉/技术中心/质量保障部/国际化,资深测试工程师,负责中台测试工作
- 雷铭峻,来自Lalamove/Tech/QA Team,高级测试工程师,负责用户端测试工作
1. 背景与挑战
在全球化的商业环境中,企业越来越重视产品的本地化适配,确保其产品满足不同地区用户的需求和使用习惯。产品的本地化不仅涉及语言和文化的多样性,还需考虑技术平台的兼容性、法律法规的合规性、用户的偏好习惯、时区、币种管理以及内容的频繁迭代变更等等。作为产品质量保障的测试工程师,如何通过层层的验证测试,给客户提供最卓越的用户体验,我们在执行本地化测试过程中面临着一系列的挑战:
-
资源限制: 本地化测试通常需要专业的翻译人员和测试工程师。然而,许多企业在这方面的资源相对有限,导致测试的质量和效率难以得到保障。
-
场景繁杂: 本地化测试涵盖多个方面,包括翻译、特殊字符处理、货币转换、时区调整、用户体验优化以及合规性测试。这些配置和切换常常耗时且复杂,容易导致遗漏,影响整体测试效果。
-
差异细微: 不同地区的用户对产品的期望和使用习惯存在差异,确保同版本产品在不同市场功能的差异准确性是一项巨大挑战。
-
技术整合:目前市场上可直接用于本地化测试的工具比较少,且无法完全满足我们的需求,需要进行定制开发。
2. 解决方案
如何减轻测试人员一遍遍的回归负担,提高测试效率变得愈发重要。除了常规的前后端功能自动化测试外,面对本地化测试的诸多痛点,让我们更加坚定了深入剖析并解决问题的决心。
综合考虑现有的测试技术框架和基础能力,我们搭建了Prism棱镜测试平台,该平台具备简洁、统一的前端操作页面,提供本地化数据测试、多市场推送测试和多语言翻译测试,以及进行中的多时区功能自动化验收,未来将致力于将其打造成为国际化综合的本地化测试平台。
3. 落地建设
接下来,我们将逐一介绍本地化测试探索过程中落地的几个工具。
3.1 本地化数据测试
随着Lalamove全球化进程的加速,数据文本类的测试也变得越来越多样化,无论是传统的UI自动化还是接口自动化测试,在发现未知数据缺陷方面的成本都变得越来越高。如何高效、准确地验证多市场数据的兼容问题成为了一个亟待解决的难题。本节展开介绍一种落地的造数技术方案,借助公司MTC UI自动化和QACI接口自动化能力,自动检测潜在的多市场数据兼容性缺陷,为自动化测试注入新的活力。
3.1.1 数据原型收集
- 用户反馈: 通过用户调研问卷、在线反馈系统、客户服务热线等渠道,收集了来自不同国家和地区的用户反馈,都是用户在实际使用过程中遇到的数据问题和建议,例如输入名称、地址、联系方式等。这些反馈往往直接反映了用户的真实需求和痛点。
- 市场调研: 利用市场调研机构的数据报告和行业分析报告,了解市场趋势、竞争对手的数据处理方式和用户偏好。这有助于我们构建更加贴近市场需求的数据原型库。
- 已知缺陷:从历史测试记录中整理出常见的缺陷数据类型,如输入方式、格式错误、字符解析、语言不匹配等。这些缺陷类型可以作为构建数据原型库的重要参考。
图:用户反馈台湾注音输入问题
3.1.2 需求分析
根据产品的功能模块和业务流程,来进一步确定我们需要测试的语言种类和文本类型。首先针对我们现有的海外市场,设计构造出不同语言的文本数据,如英语、繁体中文、泰文、越南文等。同时,我们还要考虑不同风格的文本表达,这些测试数据将用于验证APP在不同语言环境下的文本处理能力和用户体验,从而发现潜在的数据处理错误和边界条件问题。
3.1.3 工具选型
我们选用了目前市面上主流的多语言测试数据工具库Faker。近期,LOL英雄联盟著名中单选手Faker在S14全球总决赛再次带领SKT拿到队史第五座总冠军,堪称电竞界的传奇,而作为同名的Faker库在IT界也同样能力出众。这个工具库可以为我们提供丰富的语言资源和语法规则,帮助我们生成符合要求的测试数据。我们还发现Faker库支持全球大部分主流国家地区和语言,且有当地语言风格的字段设计,如街道名,邮编号码等。
图:Faker词库
除了有丰富的语言资源外,Faker还有随机生成测试数据的能力,使得我们每次跑自动化流水线的时候,验证的数据场景都是不一样的,保证了测试的多样性,能探索更多潜在缺陷的可能。
3.1.4 数据构造
在二次开发设计和构造多语言测试数据的过程中,我们首先考虑到了Lalamove用户和司机端的基本核心文本输入类型,例如姓、名、全名组合、地址、电子邮箱、电话号码等等。
Faker faker = new Faker(new Locale("en_US"));
// 生成随机名字
String firstName = faker.name().firstName();
String lastName = faker.name().lastName();
String fullName = faker.name().fullName();
// 生成随机地址
String streetAddress = faker.address().streetAddress();
String city = faker.address().city();
String state = faker.address().state();
String country = faker.address().country();
String zipCode = faker.address().zipCode();
/ 生成随机电子邮件
String email = faker.internet().emailAddress();
其次,我们还考虑到了不同国家的身份证ID或护照ID的格式不尽相同,对此我们也做了定制化设计。Faker库对此类字母数字组合提供了三种有用的解决方法分别是 Letterify、Numberify 和 Bothify。首先Letterify 有助于生成随机的字母序列;而 Numerify 仅生成数字序列;最后,Bothify 是两者的组合,可以创建随机的字母数字序列,可用于模拟 ID 字符串等内容。
FakeValueService 需要有效的 Locale 以及 RandomService:
public void generateIdentifyID {
FakeValuesService fakeValuesService = new FakeValuesService(new Locale("en-GB"), new RandomService());
String identifyID = fakeValuesService.bothify("????####??");
}
在此代码片段中,我们创建一个新的 FakeValueService,其语言环境为 en-GB,并使用 bothify 方法生成唯一的虚假身份证ID。它的工作原理是将“?”替换为随机字母,将“#”替换为随机数字。
类似地,regexify 根据所选的正则表达式模式生成随机序列。在这个单元测试代码片段中,我们将使用 FakeValueService 创建一个遵循指定正则表达式的随机序列:
@Test
public void givenValidService_whenRegexifyCalled_checkPattern() throws Exception {
FakeValuesService fakeValuesService = new FakeValuesService(new Locale("en-GB"), new RandomService());
String alphaNumericString = fakeValuesService.regexify("[a-z1-9]{10}");
Matcher alphaNumericMatcher = Pattern.compile("[a-z1-9]{10}").matcher(alphaNumericString);
}
我们的代码创建了一个长度为 10 的小写字母数字字符串,我们的模式根据正则表达式检查生成的字符串。
3.1.5 交互设计
-
传输效率: 我们采用了高效的数据传输协议和压缩算法,同时还优化了数据传输的流程和网络环境,确保数据能够及时、准确地传输到目标平台。
-
传输安全: 在进行UI自动化测试时,需要将测试数据从Prism平台传输到云测平台。为了确保数据传输的安全性和效率,我们采用了HTTPS协议进行加密传输,并设置了合理的传输间隔和重试机制。这样不仅可以保护数据的机密性,还可以确保数据能够及时到达目标平台,为测试过程提供坚实的数据支撑。
-
流程解析:
- Prism平台:为自动化平台提供了预设计的数据模板,同时为测试人员提供了友好的前端操作界面。当接收到数据构造请求后,Prism平台开始根据需求构造所需数据并生成,返回给自动化平台或测试人员使用。
- 自动化测试平台:包括移动端云测平台和接口自动化测试平台。首先根据Prism平台提供的数据模板预设好数据类型,并准备好所需的自动化场景脚本。接下来,不管是我们每天配置好的定时任务,还是手动触发的任务,只要命中了预设的多语言数据场景,就会自动向Prism平台发起接口请求。待接收到Prism生成的测试数据,则自动集成数据到我们的应用场景,并在测试报告中呈现结果。
- QA:同理,测试人员可以在业务测试过程中,使用Prism平台构造测试数据,并应用到自己的场景验证。
图:数据传输流程图
-
自动化集成
- 数据集成方案:对于UI自动化和接口自动化测试,我们设计了标准化的数据集成方案。利用已打通上下游服务壁垒的数据工厂工具将测试数据分别注入到UI界面中和接口端点,这个方案可以实现测试数据的无缝集成和自动化处理。
- 智能化:在数据集成过程中,我们也引入了智能化技术来优化数据处理流程。例如,利用机器学习算法对测试数据进行自动的分类和预测,以便更准确地识别和处理潜在的问题。
图:MTC云测平台接入语言入参配置
3.1.6 断言与结果展示
-
断言规则: 根据多语言文本在端上和端到端接口的预期结果,我们设计了详细的断言规则。这些规则涵盖了文本内容、格式、特殊符号等多个方面,以确保测试结果的全面性和准确性。例如,对于文本内容,我们要求测试数据与预期结果完全一致;对于格式和特殊符号,我们要求测试数据符合特定的规范和标准。
- 案例:在日常的UI自动化流水线任务中,需要验证用户在和司机的聊天对话框中输入的文本数据是否能够正确显示在页面上。为了实现这一目的,我们设计了相应的断言规则来检查文本内容、格式和符号等方面的一致性。如果测试数据与预期结果不一致,我们将触发断言失败并生成相应的错误报告。
图:云测平台接入预设数据请求
结果呈现
-
直观呈现:通过自动化平台的测试报告系统,我们将断言对比结果以直观、清晰的方式呈现出来。这些报告包括测试数据的详细信息、断言规则的描述、测试结果和错误信息等内容。用户可以根据报告中的信息快速定位潜在问题,为后续修复工作提供有力支持。
-
可视化分析:为了更直观地展示测试结果和趋势,我们还引入了可视化分析技术。利用图表和图形来展示测试数据的分布、缺陷类型和数量等信息,帮助用户更好地理解和分析测试结果。
- 案例:在完成一轮对巴西市场回归验证的UI自动化测试后,云测平台自动生成了相应的测试报告,并提示有一处错误。报告直观呈现了测试数据、断言规则、测试结果和错误信息等内容。浏览报告可以快速定位到问题所在:模拟巴西用户给司机的留言文本场景中,发现等待司机页面电话号码的括号不见了。由此可见,接入本地化数据检测的自动化平台可以替代人工检测出更细节的文本使用缺陷。
图:自动生成用户给司机留言文本(巴西葡萄牙语),等待司机页面电话号码括号不见了
图:通过预期断言设计,MTC云测平台自动检测出问题所在
3.2 多市场推送测试
传统的接口自动化测试通常聚焦于特定场景下的接口返回值、数据入库等方面的验证。那么, 对于测试过程中产生的推送消息要如何验证呢?推送消息的发送逻辑涉及多个组件和依赖,通常需要关注消息的生成、发送、接收和处理等环节。为了实现对推送消息的自动化验证,我们首先需要了解推送的发送逻辑及其上下游调用关系。主要包括:
- 消息生成:分析消息的构建过程,包括所需的数据格式、内容和触发条件。
- 消息发送:验证消息如何通过不同的通道(如HTTP、WebSocket等)发送,确保发送的有效性和可靠性。
- 消息接收:检查接收端如何处理这些推送消息,确保其能够正确解析和响应。
- 数据一致性:确保推送消息的内容与数据库中的数据保持一致,验证入库后的数据是否符合预期。
图:Lalamove APP 推送链路(简化)
如图所示,APP的推送逻辑由推送中心发起,经过多个下游服务,最终生成Kafka消息。根据推送策略,消息分别通过FCM或MQTT进行推送。需要注意的是,链路中并不涉及数据库写入,因此无法通过读取数据库进行校验;然而,通过消费Kafka消息则可以实现。获取推送消息的内容和状态后,我们可以设置不同的校验条件以完成自动验证。接下来,让我们看看平台是如何实现推送的自动化校验的。
3.2.1 测试场景构建
-
构建方式:
- 手动生成:研发或测试人员在日常操作 APP 过程中,可能主动或被动地生成推送消息。
- 平台生成:平台与自动化测试服务打通,在创建自动化任务时会生成任务信息,与指定推送消息进行关联
-
平台与自动化交互:
- 场景组建:基于接口自动化(Qaautotest)及数据工厂(Qatool)项目的积累,我们可以直接在自动化项目中对 Case 进行复用和重组,把一个个完整的场景放置在 不同XML 中,并通过路由的方式获取到它们
-
数据传输:交互过程中会涉及到任务数据的传输和变更,在原本的自动化框架里其实并不支持。为此,我们需要在代码中进行一些改造:
-
任务数据接收:我们采取了直接运行Jar包的方式来启动测试,需要把平台配置的参数和JVM启动参数进行映射,达到数据传入的目的
-
任务数据更新:我们使用了 TestNG 框架的监听器,把测试过程中产生的关键信息存储起来,在测试开始前及结束后进行任务数据更新,如运行状态/测试结果/报告数据等。大致流程如下
-
- 项目打包:完成上述改造后,我们通过 Maven 在本地将自动化项目编译为可执行 Jar 包。平台通过 API 请求传递任务参数,包括国家/地区名、用户/司机账户、语种和场景等,最终转化为以下命令以执行自动化
java -jar -Xms512m -Xmx512m -Xmn512m -Xss8m -Dfrom=notification -DcityId=xxxx -DreportId=739 -DdriverAccountInfo='[{"account":"+63xxxxxxxx","password":"000000","account_id":"xxxxx"}]' -Dlanguage=en_ph -DuserAccountInfo='[{"account":"+63xxxxxxx","password":"000000","account_id":"xxxxx"}]' -Denv=pre -Doperator=xxxxx -Dmarket=Philippines -DtaskName="RH Philippines Order Flow" -Did=79 -DuseDefaultAccount=false -DscenarioName=LLM_USER_ORDER_PUSH_RH -Dhcountry=xxxxx -Dstatus=2 -DxmlPath=notificationTool/user/LLM_USER_ORDER_PUSH_RH.xml -Dxml=/home/data/projects/qaautotest_llmove_user/qaautotest/target/classes/cases/notificationTool/user/LLM_USER_ORDER_PUSH_RH.xml /home/data/projects/jars/qaautotest_llmove_user/qaautotest_llmove_user.jar
- 任务限流:为了避免因并发任务过多导致服务器性能问题,我们将测试任务放入消息队列中执行,并限制了并发数量
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = RabbitMQConfig。DIRECT_NOTIFICATION_QUEUE,durable = "true",ignoreDeclarationExceptions = "true"),
exchange = @Exchange(value = RabbitMQConfig。DIRECT_NOTIFICATION_QUEUE + "_exchange"),
ignoreDeclarationExceptions = "true"),concurrency = "10")
3.2.2 场景与任务关联
接下来,我们需要把自动化与平台进行关联,使用户不仅能通过平台运行任务,还能自动校验我们需要的内容。这里涉及三个概念:推送断言/测试场景/测试任务
- 推送断言: 众所周知,校验的前提是要有预期结果,平台支持推送断言内容的灵活配置,方便多市场多统一管理
- 测试场景: 负责关联自动化/推送断言等信息,同时实现脚本步骤可视化
- 测试任务: 负责关联测试场景/市场/语言/环境/账户 等信息,同时也是执行的入口
图:测试场景构造任务
3.2.3 消息入库
无论是通过手动操作还是平台自动创建,都会生成各种消息。然而,这些消息中包含了不少无效推送,不仅数量庞大,还可能造成干扰。为了应对这一问题,我们制定了相应的存储策略。当平台接收到 Kafka 消息时,将依据既定的策略进行存储。在深入了解存储策略之前,首先让我们来看一下推送事件的不同状态:
总体而言,推送事件可以分为三种主要状态。其中,与业务相关的推送状态包括“推送成功”和“推送失败”,而“推送异常”则主要涉及设备相关问题。因此,我们的入库策略可以划分为“仅与业务相关的推送”和“全部推送”。为了减少干扰,目前主要采用第一个策略。
3.2.4 对比断言
目前分为 <实时对比> 和 <定时对比> 两种方式,同一条流量在同一时间只能参与其中一项对比。
- 实时对比: 即收到 kafka 消息后,通过过滤规则匹配(如 task_id,account_type / account_id / order_uuid/ action 等) ,如果符合指定特征,则直接进行对比。正常情况下,由平台发起的任务会执行实时对比。
- 定时对比: 非实时对比的消息(即未通过过滤规则消息),一般在数据落库时,我们会把这条数据标记为需要定时对比。定时任务周期性扫表,对满足条件的数据进行对比。
图:推送对比
3.2.5 结果轮询
在实际测试场景中,推送消息的触发方式多种多样,除了通过接口同步发送外,还包括异步处理和定时任务。这意味着,在不同的场景下,推送消息的时机可能会有所不同。因此,为了确保每个任务能够在合理的时间内完成,我们需要提前为这些任务配置超时时间。例如,在用户注册成功后,系统会在 3 分钟内发送一条营销消息给用户。因此,为了符合这一逻辑,我们必须配置至少 3 分钟的超时时间。
当定时任务执行时,如果检测到本次测试已经超时,系统将会根据推送的断言情况记录最终的测试结果。
3.2.6 结果推送
测试完成后,通过飞书机器人推送结果,并附上任务链接,方便查询。
3.3 多语言测试
在Lalamove的日常功能迭代中,经常涉及到不同市场的翻译验证,相同场景需要切换不用的地区和语种做多次检查。为此,我们开发并不断优化了多语言测试工具,提供不同的自动校验功能,并将其也集成在Prism本地化测试平台上,提供最简洁的使用操作。
3.3.1 语种校验
在翻译文案的测试过程中,我们发现最常见的是未翻译,即某些场景的key对应的还是默认语种英文,并未翻译为当地语言,为了花较小的成本批量发现此类问题,在每次提测时,我们都对翻译内容进行一次全量的语种检测。
- 实现方法:
为了适配不同的国家和地区,灵活的切换展示语言,翻译内容多以配置文件的方式存储,通过key来匹配不同场景下的翻译内容,例如下面的登陆页面:
其翻译原文件如下:
<string name="app_global_log_in">Đăng nhập</string>
<string name="app_global_register">Đăng ký</string>
我们的自动化校验工具通过解析翻译原文件,获取翻译文本,然后对翻译文本进行全量校验,检查其语种是否符合预期,如果不匹配,检查是否是因为一些运营原因需要保持非本地语言的翻译,最终输出检查结果,落库存储,经由报告页面解析展示。
- 检测原理
判断语种是否匹配时,使用基于N-Gram的语种检测模型,其实现原理如下:
-
构建语料库:基于wiki, twitter提取每个语种的常用语句
-
预处理:对文本进行分词、去除停用词等操作,得到一系列单词或符号序列。
-
构建N-Gram:对处理后的文本进行大小为N的滑动窗口操作,形成长度为N的单词或符号片段序列。对于每个片段序列,统计其出现的次数。
-
过滤和选择关键gram:根据出现频度对gram进行过滤和选择关键gram。通常使用事先设定好的阈值进行过滤,选择出现频度高于阈值的gram作为关键gram。
-
构建向量特征空间:将关键gram转换为向量形式,形成一个向量特征空间。向量维度即为关键gram的种类数量。
-
报告展示
3.3.2 翻译对比
翻译内容对比的数据源同语种检测,区别在于解析完翻译数据后,对格式化后的翻译数据进行对比,如果实际翻译与目标翻译内容匹配,则校验通过。目标翻译内容包括:
- 不同分支:每次迭代的时候检测本次发布分支和上一发布分支的差异,避免在翻译同步过程中引入不符合预期的改动;
- 翻译文件:由产品提供的翻译文件,以csv,excel等文件格式存储,对比代码中的翻译内容和产品原始的翻译内容是否一致;
3.3.3 拼写校验
翻译内容对比的数据源也与上述两种校验相同,对解析后的翻译内容进行全量的拼写校验并输出校验结果。拼写检查主要检查两部分:
3.3.4 长文本校验
长文本校验,是对翻译解析后的内容根据制定的阈值,检测超过阈值的翻译文本。阈值根据不同的国家和市场,可动态调整,如下面的文本,在语言为英文时只占两行,切换到越南语,需要三行去展示,如果前端的渲染是基于英文去开发调试,那么切换到越南就会有文本展示异常的风险,针对越南语我们设置的阈值要高于英文,去检查同样场景下的长文本。
4. 未来展望
本地化测试已初见成效,但这条路依然很长很远,未来我们将从以下几个方面继续丰富本地化测试的广度和深度:
- 继续深化平台工具的研发和优化工作,提升测试数据的准确性和多样性;
- 加强与其他形式自动化测试的协作,构建更加完善的测试生态体系;
- 丰富本地化检测类型,例如自动化货币/合规性检测;
- 翻译长文本检测结合UI自动化,自动完成长文本展示页面的截图,减少人工测试成本;
- 引入更先进的机器翻译和自然语言处理工具,自动化翻译质量评估,减少人工干预。