兼容性与本地化测试

共 17 题
📑 题目列表 17 题
#
★★★

1. 字符编码边界测试(UTF-8 多字节字符、emoji、组合字符、零宽字符、BOM)如何系统化设计?请举例说明每种字符类型可能引发的典型缺陷。

字符编码边界测试(UTF-8 多字节字符、emoji、组合字符、零宽字符、BOM)如何系统化设计?请举例说明每种字符类型可能引发的典型缺陷?

  • 各类 Unicode 字符的编码特性与存储差异
  • 各种字符类型引发的典型缺陷模式
  • 编码边界测试用例的系统化设计

系统化设计各类字符的边界用例,覆盖:多字节字符——如中文"中"(E4 B8 AD)、日文假名、韩文等,典型缺陷是字符串长度按字节而非字符截断导致半个字符、乱码或数据库字段溢出;emoji——位于补充平面(4 字节 UTF-8),典型缺陷是数据库 utf8(3 字节)无法存储、按 code point 或按游标单位(UTF-16 的 surrogate pair)截断导致破相;组合字符——如字母+组合变音符号(e + ́),典型缺陷是渲染时宽度不一致、排序/搜索时规范化(NFC/NFD)不一致导致匹配失败;零宽字符——零宽空格、零宽连接符,典型缺陷是字符串对比失真、被当作隐藏字符注入、显示异常;BOM——文件头部的 BOM 字节被当作内容显示/解析,典型缺陷是第一个字段出现不可见字符导致解析或比较失败。设计时应覆盖"存储、传输、显示、比较、截断、排序"全链路的字符边界。

编码缺陷往往在"跨界"处爆发:存储层(字节数)、传输层(编码声明)、显示层(无法渲染)、比较层(规范化)。测试要以"字符类型 X 字符集"为矩阵,覆盖每个环节的边界。

// 判断字符串是否被字节截断(出现孤立的代理对)
String s = "用户😀下单";
long charCount = s.codePointCount(0, s.length()); // 按 Unicode 字符数
long byteCount = s.getBytes(StandardCharsets.UTF_8).length; // 按字节数
#
★★★

2. 伪本地化(Pseudo-Localization)测试的原理和实施步骤是什么?它如何在不等待翻译的情况下发现硬编码字符串和布局溢出问题?

伪本地化(Pseudo-Localization)测试的原理和实施步骤是什么?它如何在不等待翻译的情况下发现硬编码字符串和布局溢出问题?

  • 伪本地化的原理(字符替换、扩展、标记)
  • 实施步骤
  • 在翻译前发现硬编码与布局溢出的价值

伪本地化(Pseudo-L10N)是把英文源字符串替换为"伪翻译"文本的测试手段:把普通字符替换为带重音/变音符号的字符,并有意把字符串长度加长(如加 30%~50%),从而模拟翻译后的文本在长度、字符集上的真实情况。实施步骤:用伪本地化工具(如苹果的 pseudolocalization 工具、IBM 的 Pseudo-Link、或自写的替换脚本)生成伪本地化版本,开关(如命令行参数或内部标志)启用后再运行 UI 测试与截图对比。它能在没有真实翻译的情况下,提前暴露硬编码字符串(未走资源文件、直接写死在代码里的文本不会继承伪本地化而保持英文,从而被一眼发现)和布局溢出(长度变长后文本被截断、按钮错位、换行异常)。

伪本地化的核心价值是"把翻译的未来风险提前到今天",并且覆盖"国际化"的完整性——凡是没走 i18n 资源机制的字符串,其文本不会变,从而可被识别。它是 i18n 测试的高性价比前置手段。

#
★★★

3. RTL 语言(阿拉伯语/希伯来语)布局镜像测试需要验证哪些元素?除了文本方向,还有哪些 UI 元素需要镜像处理?

RTL 语言(阿拉伯语/希伯来语)布局镜像测试需要验证哪些元素?除了文本方向,还有哪些 UI 元素需要镜像处理?

  • RTL 布局镜像的核心原则
  • 文本方向与 UI 元素镜像的区分
  • 双向文本(BiDi)处理

RTL 布局镜像测试需验证:文本对齐方向——从右到左显示,句子起始位于右侧;图形元素镜像——图标、箭头、进度条方向、返回按钮、滑块、翻页方向都应左右翻转;布局顺序——导航、列表、表单字段的起始位置(如最早字段在右侧);数字与内嵌拉丁文本——阿拉伯数字可保持 LTR,但嵌入的英文/数字混排需按 BiDi 规则处理;时间线与图表——时间轴方向、reading order 反转。还需验证双向文本(BiDi)中混合 LTR 片段的显示顺序,以及触摸/手势方向(如向左滑动是"下一页")。测试时应截图对比 LTR 与 RTL 两种布局,重点检查镜像遗漏导致的误读。

RTL 不只是"文本右对齐",而是"整个布局镜像"。测试要同时覆盖文本方向、图标/控件镜像、手势方向与 BiDi 混排,避免只关注文本而遗漏图形与交互的镜像。

#
★★★

4. 浏览器/操作系统/设备兼容矩阵如何制定与排优先级?请说明基于市场覆盖率、业务数据和用户画像的优先级排序方法。

浏览器/操作系统/设备兼容矩阵如何制定与排优先级?请说明基于市场覆盖率、业务数据和用户画像的优先级排序方法?

  • 兼容矩阵的制定维度
  • 基于数据(市场覆盖率、业务数据、用户画像)的优先级排序
  • 覆盖率与成本(测试金字塔)的平衡

兼容矩阵的制定应基于数据而非拍脑袋:先收集市场覆盖率(如 StatCounter/浏览器份额统计)、业务数据(各平台上的访问量、下单量、转化率、营收占比)、用户画像(目标人群的设备和浏览器习惯、地区分布)。据此把组合按"重要度×影响"排序:高份额且高转化的平台定为最高优先级(全量回归),中份额的定为中等优先级(核心功能回归),低份额或边缘平台的定为低优先级(冒烟或不测)。同时考虑成本与风险:对无法覆盖的旧版本,评估其用户占比与故障影响,决定是降级还是完全放弃。最终形成"按优先级分层"的兼容矩阵,并随数据变化定期更新。

兼容矩阵的本质是"风险决策":没有足够预算覆盖所有组合,必须用数据把资源投入到影响最大的组合上。优先级排序的输入是市场数据+业务价值+用户画像,输出是分层测试策略。

#
★★★

5. 多版本 API 并存时的向后兼容测试策略是什么?SemVer(语义化版本)规范如何指导兼容性测试设计?

多版本 API 并存时的向后兼容测试策略是什么?SemVer(语义化版本)规范如何指导兼容性测试设计?

  • 向后兼容测试的策略(版本并存、兼容层)
  • SemVer 的版本号语义与兼容性承诺
  • 兼容性测试如何随版本演进设计

多版本 API 并存时,向后兼容测试要保证新版本不破坏旧版本客户端。策略包括:版本化(URL/头/参数带版本号)并存多版本接口,对新版本做回归、对旧版本做兼容回归;用兼容层/适配器保证缺省行为一致;对新老版本跑同一套契约测试,验证字段增删、默认值、错误码等不破坏旧客户端。SemVer(X.Y.Z)规范:主版本号(X)不兼容变更、次版本号(Y)向后兼容的新功能、修订号(Z)向后兼容的修复。它指导测试设计——主版本变更时才允许破坏性变更,需重点做不兼容回归与迁移指引;次版本/修订号变更必须向后兼容,测试要验证"旧客户端仍能工作"(字段可空、默认值、响应结构兼容)。

向后兼容测试的核心是"旧客户端在前向运行"的保证。SemVer 把"什么变更允许破坏"用版本号显式化,测试据此分配验证力度:主版本升级重测迁移,次/修订升级必测兼容。

#
★★★

6. 浏览器渲染引擎差异(Chromium/WebKit/Gecko)导致的兼容性缺陷模式与专项用例设计

浏览器渲染引擎差异(Chromium/WebKit/Gecko)导致的兼容性缺陷模式与专项用例设计有哪些?

  • 三大渲染引擎的差异来源
  • 常见渲染兼容缺陷模式
  • 专项用例设计思路

渲染引擎差异(Chromium/Blink、WebKit/Safari、Gecko/Firefox)导致的缺陷模式常见有:CSS 属性与布局差异——如 flex/grid 的某些子属性、position: stickyaspect-ratio 在不同引擎支持与实现不一致,导致布局错位;JS 行为差异——Date.parseIntl 格式、ES 特性的支持差异导致脚本报错;媒体与字体——fitsource 属性、字体回退、object-fit 支持不一;滚动与触摸——scroll-behavior100vh 视口高度、-webkit 前缀行为差异;打印与剪贴板——打印样式、navigator.clipboard 权限差异。专项用例设计:按引擎建立"特性×版本"矩阵,用 feature-detection 辅助;对每个核心页面在不同引擎下跑视觉对比(截图 diff)与功能冒烟;对 CSS 特性用 caniuse 数据筛选差异点单独设计用例;对 JS 用 polyfill 兼容测试。

引擎差异是版本对特性的"实现与支持"不同。专项测试应结合特性支持矩阵(caniuse)与真实引擎运行,用截图对比和功能冒烟覆盖"相同代码不同呈现"的差异,而非只测单一浏览器。

#
★★

7. Locale 相关的日期/时间/货币/数字/排序格式如何系统化测试?ICU/CLDR 数据在测试中的作用是什么?

Locale 相关的日期/时间/货币/数字/排序格式如何系统化测试?ICU/CLDR 数据在测试中的作用是什么?

  • 各类 Locale 格式的测试维度
  • 日期/货币/数字/排序的格式化差异
  • ICU/CLDR 数据的作用

系统化测试 Locale 格式需覆盖:日期/时间——不同地区用 ISO-8601、月/日/年顺序、12/24 小时制、星期起始、时区显示;货币——货币符号位置(前置/后置)、千位分隔符、小数位、负数表示(- 号/括号/颜色);数字——小数点与千位分隔符(1,234.56 vs 1.234,56);排序——不同语言照字母/拼音/笔画排序。对每个 locale 生成"格式化输出快照"做断言,并与期望值对比。ICU/CLDR(Unicode Common Locale Data Repository)是权威的 locale 数据仓库,提供各地区的格式规则;ICU 是库,提供格式化 API。测试时以 CLDR 数据为基准比对程序输出,可发现"程序硬编码了格式"与"locale 数据缺失"的问题,并验证 ICU 版本升级后格式变化导致的回归。

Locale 格式测试的核心是"以标准数据为基准的断言"。ICU/CLDR 提供了权威的格式规则,既能指导期望值,也能在 ICU 升级时预警格式回归。测试应覆盖"显示、解析、往返(parse→format)"。

#
★★

8. 时区与夏令时(DST)切换在跨区系统中的测试要点有哪些?如何构造 DST 切换瞬间的边界测试场景?

时区与夏令时(DST)切换在跨区系统中的测试要点有哪些?如何构造 DST 切换瞬间的边界测试场景?

  • 时区/DST 的测试维度
  • DST 切换瞬间的边界情况(模糊时间、重复时间、不存在的本地时间)
  • 用稳定性的时区数据构造边界场景

跨区系统的时区/DST 测试要点包括:存储与显示分离——数据库通常存 UTC,展示时按用户时区转换,要验证转换正确;时区偏移随时间变化——同一时刻在不同时区/不同 DST 状态的偏移不同;DST 切换瞬间的三种边界——"模糊时间"(秋季回拨时某本地时间出现两次)、"不存在的本地时间"(春季前拨时某本地时间被跳过)、"重复时间"(同一时刻对应两个可能的本地时间)。构造方法:用明确指定时区的 DateTime(如 ZoneId.of("America/New_York"))和 Clock 注入固定到切换瞬间的时钟,验证边界时刻的解析、存储与显示;测试跨 DST 的时长计算(如会议跨越切换日)、定时任务(cron 在回拨日是否重复触发)、以及"本地时间→UTC"和"UTC→本地时间"的往返一致性。

时区测试的难点是"时间不是单调的"。DST 切换出现"多/无/重复"的本地时间,必须用可注入的时钟构造这些边界,而非依赖真实系统时间。要验证往返一致性与跨 DST 的时长计算。

// 用固定时钟注入构造 DST 边界场景
ZoneId ny = ZoneId.of("America/New_York");
ZonedDateTime fallBack = ZonedDateTime.of(2024, 11, 3, 1, 30, 0, 0, ny);
// 该本地时间在回拨日出现两次(EDT 与 EST),需验证系统如何处理重复/模糊时间
#
★★

9. 翻译文本截断/溢出如何自动化检测?字体与字形回退(Fallback)在 CJK 和印度系文字中的测试策略?

翻译文本截断/溢出如何自动化检测?字体与字形回退(Fallback)在 CJK 和印度系文字中的测试策略是什么?

  • 文本溢出/截断的自动化检测手段
  • 字体回退的机制与缺陷
  • CJK 与印度系文字的特殊性

翻译文本截断/溢出自动化检测常用:截图对比(visual diff)——对翻译后的页面截图与基准图对比,检测文本溢出、被截断、重叠;布局测量——用脚本读取元素的实际尺寸、是否超出容器、text-overflow 情况;收集未翻译/截断提示——如检测 省略号、??? 或乱码。字体回退(Font Fallback)测试:当某字形(glyph)在首选字体中缺失时,系统回退到其他字体,需验证回退后仍可读、不出现豆腐块(□)。策略上,CJK 测试需验证中、日、韩文字的字体覆盖与宽度(CJK 全角字符)、混排时避免"字形缺失";印度系文字(如梵文、泰文)需验证复杂文字布局(撮合、连字、双向重排),不同字体回退可能导致连字断裂或乱序,需专项测试识别与渲染。

溢出检测的核心是"以视觉和布局为基准的自动化对比"。字体回退的关键是"字形缺失时的可读性",CJK 和印度系文字因复杂字形与连字规则,对回退更敏感,需专项验证。

#
★★

10. 灰度升级中新旧客户端并存如何验证兼容?请说明数据格式兼容、协议兼容和行为兼容三个维度的测试方法。

灰度升级中新旧客户端并存如何验证兼容?请说明数据格式兼容、协议兼容和行为兼容三个维度的测试方法?

  • 灰度升级中兼容性的三个维度
  • 数据格式兼容、协议兼容、行为兼容的测试方法
  • 新旧客户端并存的验证策略

灰度升级会存在新旧客户端并存访问同一后端的场景,需从三个维度验证:数据格式兼容——新旧版本读写的数据结构(如 JSON 字段、数据库表结构)要互相兼容,测试新版本写入的数据旧版本能读、旧版本写入的数据新版本能读,字段增删不影响旧解析;协议兼容——新旧客户端调用的 API 协议(版本、参数、返回结构)兼容,测试旧客户端调新接口、新客户端调旧接口均正常;行为兼容——新旧版本的业务逻辑(默认值、错误处理、状态流转)在并存时行为一致,测试同一用户在不同版本下的操作结果一致。排期上,灰度期先只让新版本只读、再读写,分阶段验证三方面兼容,最后全量切换。

灰度兼容的本质是"新旧版本同时在线的平滑过渡"。三个维度分别对应数据结构、通信协议、业务行为,测试要覆盖"旧读新写、新读旧写"的交叉组合,才能保证回滚与并存安全。

#
★★

11. 依赖库/运行时版本(JDK/Node/Python)兼容性回归如何系统化执行?'依赖矩阵爆炸'问题如何缓解?

依赖库/运行时版本(JDK/Node/Python)兼容性回归如何系统化执行?"依赖矩阵爆炸"问题如何缓解?

  • 运行时/依赖版本兼容回归的执行方式
  • 依赖矩阵的概念与爆炸问题
  • 缓解矩阵爆炸的策略

运行时/依赖版本兼容回归系统化执行:用 CI 矩阵构建在多个版本组合下跑测试(如 GitHub Actions 的 matrix 策略,JDK 8/11/17 或 Node 14/16/18),对每个版本跑编译、单元测试与关键集成测试;用容器(Docker 多版本镜像)隔离版本环境;对关键依赖升级自动触发回归。问题在于"依赖矩阵爆炸"——如果同时组合操作系统、运行时版本、依赖版本、架构,组合数指数增长,测试成本不可控。缓解策略:只测试"受支持的最低版本 + 最新版本 + 主流的中间版本"(边界+代表性),而非全组合;用"运行时版本矩阵"与"依赖版本交叉"分离,优先测运行时版本,依赖用兼容性扫描;用测试分片与并行加速;把低风险组合降级为冒烟。

"依赖矩阵爆炸"的根因是把所有维度全组合。缓解的关键是"降低组合数"——用边界版本+代表性版本代替全组合,并分离维度、并行加速,把测试成本与覆盖度平衡起来。

#
★★

12. 数据迁移兼容性测试,如何验证旧版本数据在新版本中正确读取,新版本数据在降级后仍可回退?

数据迁移兼容性测试:如何验证旧版本数据在新版本中正确读取,新版本数据在降级后仍可回退?

  • 数据迁移兼容的正向与反向验证
  • 前向兼容(旧数据→新版本)与回退(新数据→旧版本)
  • 迁移脚本与回滚的测试

数据迁移兼容性测试需要双向验证:正向(前向兼容)——用旧版本的真实数据样本在新版本中读取,验证字段映射、默认值、类型转换、新增字段填充都正确,且旧数据不因迁移而丢失;反向(回退)——新版本写入的数据在降级到旧版本后仍可读取,验证新增字段在旧版本中被忽略或提供合理默认值、不因字段缺失而崩溃。同时要测试迁移脚本本身:迁移的可重复性(幂等)、可回滚性(回滚脚本能恢复旧结构)、迁移中途失败的一致性(事务回滚)。测试数据应覆盖旧版本全特征(含历史遗留异常数据),并做迁移前/后数据一致性对账。

迁移兼容的难点是"时间不对称":旧数据要能被新版本读,新数据要能在回退时被旧版本读。测试要建立双向数据样本,并验证迁移脚本的幂等、回滚与失败一致性。

#
★★

13. 低端设备、低内存与弱网条件下的兼容性测试,资源受限环境的验证维度如何设计

低端设备、低内存与弱网条件下的兼容性测试:资源受限环境的验证维度如何设计?

  • 资源受限环境的验证维度
  • 低内存、低端设备、弱网的具体测试点
  • 性能与稳定性在受限环境的表现

资源受限环境的验证维度设计:低端设备——CPU 较弱、GPU 能力低、屏幕分辨率小,验证动画流畅度、字体渲染、触摸响应、页面加载速度;低内存——验证内存峰值、OOM 崩溃、图片/列表的回收与缓存淘汰、后台恢复时的状态保持;弱网——用网络节流(如 Chrome DevTools 的 Slow 3G、network link conditioner)模拟高延迟、低带宽、丢包,验证超时处理、重试、加载占位、离线缓存、断点续传。还需验证在资源受限下"降级体验":如图片降分辨率、关闭高清、减少动画,保证功能可用而非崩溃。设计上按"设备档位×网络档位"矩阵,用真实或模拟设备跑关键链路与稳定性(长时间运行、内存监控)。

受限环境验证的核心是"在资源边界下不崩溃、可用、可降级"。其维度是设备性能、内存、网络三类资源,测试要覆盖极限负载下的稳定性与降级路径,而非只看高端设备。

#
★★

14. 操作系统与硬件架构兼容性(x86/ARM、Windows/Linux/macOS)的测试矩阵与优先级

操作系统与硬件架构兼容性(x86/ARM、Windows/Linux/macOS)的测试矩阵与优先级如何制定?

  • 操作系统与架构的测试矩阵维度
  • 优先级排序方法
  • 交叉编译与架构差异的测试

操作系统与硬件架构的测试矩阵需考虑两个维度:操作系统(Windows/Linux/macOS)与硬件架构(x86_64/ARM64/AArch64)。测试矩阵的制定:主流的 OS×架构组合,如 Windows x86_64、Linux x86_64、Linux ARM64、macOS ARM64(Apple Silicon)为高优先级;探针性的组合(如 Windows ARM、BSD 等)为低优先级。ARM 架构(尤其 ARM64)因指令集、字节序(通常小端)、浮点支持、内存模型差异,可能暴露 x86 上不出现的缺陷(如 unaligned access、endianness、依赖特定指令的汇编)。优先级排序依据用户分布与部署目标(如服务器以 Linux x86_64 为主、移动/边缘以 ARM 为主)。测试应覆盖:编译/链接、原生库加载、系统调用、内存布局、及跨架构的字节序与对齐。

架构差异常在"native 代码与底层"暴露,因为上层语言(如 Java/JVM)跨架构较一致,但 native 库、汇编、字节序、对齐是重灾区。优先级按"部署目标×架构"并结合用户分布排序。

#

15. 国际化(i18n)与本地化(l10n)的根本区别是什么?测试团队在哪个阶段介入最具成本效益?

国际化(i18n)与本地化(l10n)的根本区别是什么?测试团队在哪个阶段介入最具成本效益?

  • l18n 与 l10n 的区别
  • 测试介入时机与成本效益
  • 全生命周期测试(全球化测试)的理念

国际化(i18n,Internationalization)是让产品"具备支持多语言/多地区的能力",如把字符串抽离到资源文件、支持 Unicode、按 locale 格式化日期货币、提供 RTL 布局能力,是开发层面的工程改造;本地化(l10n,Localization)是在已有国际化能力的基础上,针对特定地区/语言提供翻译文本、本地化内容与相应调整,是内容与适配层面的工作。测试团队应在 i18n 阶段(设计早期)就介入最具成本效益——因为国际化缺陷(硬编码字符串、未拆分的格式、编码问题)一旦嵌入大量代码,后期修复成本极高;在 l10n 阶段再介入则翻译错误、布局溢出等会反复出现。理想做法是"全球化测试"(G11N)贯穿整个生命周期:国际化前做文化审查、国际化中做伪本地化与格式测试、本地化后做语言与布局回归。

区别是"能力"与"执行":i18n 是基础设施能力,l10n 是具体地区的落地。越早介入(i18n 阶段)成本越低,因为 i18n 缺陷是结构性的、修复成本高,而 l10n 缺陷是相对局部的。

#

16. 多语言环境下的排序(Collation)规则差异如何测试?请以中文拼音排序和笔画排序为例说明。

多语言环境下的排序(Collation)规则差异如何测试?请以中文拼音排序和笔画排序为例说明?

  • Collation 的语言差异与测试方法
  • 中文拼音排序与笔画排序的差异
  • 排序与 locale 数据的关联

多语言排序(Collation)测试需验证不同 locale 下字符串的排序结果符合该地区的规则。测试方法:对每种语言构造一组覆盖排序规则的测试词,调用排序 API 后断言输出顺序与期望一致。以中文为例,拼音排序——按汉字拼音的字母顺序排列(如"安全"排在"博客"前),需考虑多音字、声调、拼音首字母;笔画排序——按汉字笔画数、部首、笔顺排列。两者结果不同,同一组词在不同 locale/排序规则下顺序可能不同。测试要点:选择 locale 正确的排序器(如 Collator.getInstance(Locale.CHINA) 用拼音,Locale.TAIWAN/zh_Hant 用笔画或注音)、验证大小写、重音、变音符、基线(如拼音顺序)的处理、以及对合并字符与多音字的处理。排序结果应随 Unicode 排序权重(CLDR collation)一致。

排序的难点是"规则因语言而异且没有统一顺序"。测试要针对每个 locale 用"有代表性的测试词"验证排序结果,并关注中文拼音/笔画、多音字等特殊规则,以 CLDR collation 为基准。

// 中文拼音排序 vs 笔画排序
java.text.Collator pinyin = java.text.Collator.getInstance(java.util.Locale.CHINA);
String[] words = {"安", "博", "测"};
java.util.Arrays.sort(words, pinyin); // 拼音序
#

17. 浏览器兼容性测试中,自动化截图对比与功能行为测试各自的覆盖盲区是什么?

浏览器兼容性测试中,自动化截图对比与功能行为测试各自的覆盖盲区是什么?

  • 截图对比与功能行为测试的覆盖范围
  • 各自的盲区(局限)
  • 两者结合的必要性

自动化截图对比(visual diff)覆盖"视觉呈现"——布局、颜色、字体、图片、像素级差异,适合发现视觉回归与渲染差异,但盲区是"不验证功能":页面看起来正常但按钮点击无响应、表单提交失败、JS 报错等都无法被截图察觉;且对动态内容、动画时序、非确定性渲染(异步加载、字体加载)易产生误报。功能行为测试(functional test)覆盖"交互与逻辑"——点击、输入、跳转、数据提交、状态变化,但盲区是"不验证视觉":控件可用但布局错位、文字截断、颜色对比不足等视觉问题不会被发现;且对纯视觉差异(如字体未加载、样式缺失)不敏感。两者结合才能互补:截图对比保"看起来对",功能测试保"用起来对"。

两者的盲区恰是对方的强项:截图对比看不见"行为",功能测试看不见"长相"。兼容性测试需同时用两者,并处理截图对比在动态/异步内容下的稳定性问题。