前面我们介绍测试的时候其实说过测试分类这个事情,但当时介绍的分类不太全面,今天我们就来更全面地认识一下有哪些种类的测试
按照测试目标分类
界面测试
界面测试要求我们对产品界面中任何肉眼可见的元素进行测试,比如按钮的点击,输入框输入文本,下拉框的选择,其他的交互等等
前端开发人员在执行开发之前,交互/设计同学会先给出设计图,所谓的设计图就是以图片的形式展示要实现的前端页面,在设计图会明确给定界面中每个元素大小、形状、颜色等等要素。然后前端人员就根据这个设计图开发出具体的前端页面。
那是不是开发出的前端页面就一定和设计图一模一样呢?肯定不是,有些可能是技术原因没法实现,有些可能确实是前端大意,没能做好。为了找出那些因为前端大意导致的UI BUG,就需要我们测试人员进行UI 界面的测试,具体的测试用例可以围绕下面几个角度来设计
-
验证界面对屏幕大小的自适应性
-
验证整个界面布局和排版是否合理
-
检测界面不同控件的大小的形状是否合适
-
检测界面的布局和色调是否合适
- 一般网站都有自己的主体色,比如淘宝是橙色,京东是红色
- 你要是在淘宝界面中加入一个绿色的控件,这显然就是不合适的
功能测试
功能测试就是对产品的软件需求中规定的各项功能进行验证。具体来说就是先针对功能,设计相应的测试用例,然后逐项测试,检查产品是否达到用户要求的功能。
如何设计测试用例?
黑盒设计测试用例的方法:等价类、边界值、判定表法、正交法、场景法、错误猜测法等(前面都说过,这里不再多说)
性能测试
广义的性能测试,就是测试产品在极端条件下的各项性能。比如测试油门一脚踩死时汽车的加速时间,检测塑料杯装开水会不会释放有毒物质等等
对于互联网产品来说,它的性能测试主要是通过测试产品在不同并发量下的响应时间、吞吐量、错误率,寻找产品的性能拐点和性能极限
可靠性测试
这个其实也可以把它理解成稳定性测试,假如说我对A产品进行了10000次测试,其中有10次测试请求出错了,未能得到预期结果。然后我对B产品也进行了10000次测试,其中只有一次测试出错。那我们就会说,B产品运行起来比A产品更稳定,或者说更可靠
具体测试就是计算在给定测试总量下产品的出错率,出错率越低,越可靠。
可靠性指标=1-出错率。可靠性指标(准确率)一般要求达到4个或5个“9”,即99.99%或者99.999%
- 如果可用性达到99.99%,对于一个全年不间断(7*24的方式)运行的系统,意味着全年(252600min)不能正常工作的时间只有52min,不到一个小时。
- 如果可用性达到99.999%,意味着全年不能正常工作的时间只有5min。
安全性测试
简要介绍
安全性测试主要是测试系统中是否存在安全漏洞,以及测试系统面对黑客病毒攻击时的安全防护能力。
你安全性测试中的安全,其实指的就是信息安全与系统安全,信息安全就是你这个系统中用户的隐私数据不能被第三方获取,系统安全指的是你这个系统在遭受各种黑客和病毒攻击之后会不会崩。因此我们在设计测试用例的时候就可以从这些方面来考虑
-
用户信息安全
- 用户的隐私数据会不会在传输的过程中被第三方成功截获?
- SSL安全层协议,对称加密与非对称加密
- 用户的隐私数据会不会在服务器端被第三方非法获取?
- 数据库中用户密码有没有做加密存储
- 用户的数据会不会被越权访问(系统的权限管理能力)
- 用户的隐私数据会不会在客户端被第三方非法获取?
- 检测登录框中用户的密码有没有做隐式回显
- 用户的隐私数据会不会在传输的过程中被第三方成功截获?
-
系统抵御黑客/病毒攻击的能力
- 检测是否存在sql注入的安全漏洞
- 检测是否存在xss漏洞
常见的系统安全漏洞
系统常见的安全漏洞和威胁如下
- 输入框中输入恶性或者带有病毒的脚本或长字符串;
- 代码中的安全性问题,如SQL/XML注入
- 不安全的数据存储或者传递(信息在传输以及存储的时候没有加密)
- 数据库中有危害系统的信息或者数据;
- 访问控制做的不到位,不能有效阻止越界访问
- 权限设置不合理,给某个普通用户的权限太高,导致它能轻而易举地偷走系统中的核心数据
- 第三方假冒用户ID——身份欺骗
- 中间人截获传输信息,对数据进行恶意修改,破坏数据的完整性
安全性测试的方法
安全性测试的方法主要有代码评审,渗透测试,安全运维三种
- 代码评审:属于静态安全测试,通过人工或工具对代码进行审查,提前发现代码中的安全缺陷(比如代码是否存在SQL注入、XSS漏洞问题)
- 渗透测试:属于动态安全测试,模拟黑客攻击行为,主动尝试突破系统安全防护,验证系统在真实攻击场景下的抗风险能力。
- 安全运维:是贯穿系统全生命周期的安全管理手段,包括安全配置检查、日志审计、漏洞补丁管理等,保障系统长期处于安全运行状态。
常用的静态安全测试工具有,Coverity,IBM Appscan Source,HPFortify,常用的动态安全测试有OWASP的ZAP,HP WebInspect等。其中静态安全测试是常用的安全性测试的方法。
即安全如此重要,咱们的私人电脑需要安装杀毒软件吗……(铁汁多少有点抬举自己了嗷~)
安全性测试用例设计
对于一个具体的系统,我们可以从以下角度来编写测试用例
-
身份认证与授权
- 登录、注销机制是否安全
- 权限分级是否严格(普通用户不能访问管理员功能)
- 密码策略是否合理(复杂度、有效期、锁定机制)
-
数据传输安全
- 是否使用加密(如 HTTPS/TLS)
- 敏感数据在传输过程中是否会被窃取或篡改
-
数据存储安全
- 数据库、文件等是否加密存储
- 备份和恢复机制是否安全
-
常见漏洞检测
- SQL 注入
- XSS(跨站脚本攻击)
- CSRF(跨站请求伪造)
- 文件上传漏洞
- 命令注入
- 目录遍历
-
业务逻辑安全
- 是否存在绕过业务流程的漏洞(如跳过支付步骤)
- 接口调用是否校验合法性
-
安全配置与日志
- 服务器、数据库、中间件的安全配置是否合理
- 安全日志是否完整记录关键操作和异常行为
-
压力与容错
- 系统在遭受 DDoS 或高并发攻击时的稳定性
- 异常输入、越权访问等是否会导致崩溃
易用性测试
易用性测试就是测试你这个软件容不容易上手。那如何判断一个软件容不容易上手呢?我们主要从下面三个方面考虑
是否符合行业标准规范
比如行业规定:游戏首次登录需要进行实名认证+人脸识别,你这个软件在测试时要是发现没有,这就是一个测试的BUG。
再比如行业规定:新用户注册后第一次进入软件,必须为其提供新手引导,你这个软件要是没有,恭喜测试人员又找到了一个测试BUG
当然很多时候行业标准规范的制定并不是为了方便你上手,但不管出于啥目的,测试人员都必须考虑这一块
操作的灵活性
像b站的一键三连,qq动态的一键转发,这都是增强产品灵活性的典型案例
直观性与舒适性
这个就很主观了,就是测试人员亲自去上手测试,看看你这款产品的界面是否直观,用起来舒不舒服
按照执行方式分类
静态测试
所谓静态测试(static testing)就是不实际运行被测软件,用瞪眼法检查程序代码、界面或文档中可能存在的错误
比如新手写C++代码时很容易出现的内存泄露问题,为了避免内存泄露,老手在写代码的时候就会留心每一个需要手动释放的对象,确保对象在程序结束之后被及时释放,所占内存在程序结束之后被及时回收。其中老手边写代码边检查的方法就属于是静态测试
静态测试主要通过分析或检查源程序的设计、内部结构、逻辑、代码风格和规格等来检查程序的正确性。
常见的静态测试方式有:
- 代码走查(领导开会,组织大家一起检查)
- 代码扫描工具(sonnar)
动态测试
动态测试(dynamic testing),指的是实际运行被测程序,输入相应的测试数据,检查实际输出结果和预期结果是否相符的过程。这其实也是OJ判题的基本流程
所以判断一个测试属于动态测试还是静态的,唯一的标准就是看是否运行程序。大多数软件测试工作都属于动态测试
按照测试方法分类
白盒测试
什么是白盒测试?
白盒测试中的白盒是啥意思呢?
白盒的意思就是——测试对象的内部是可见的
那白盒测试是干啥的呢?
既然测试对象内部都可见了,那我在测试的时候测啥呢?我们要测试代码的运行逻辑是否符合预期。
说到这里,是不是有种似曾相识的感觉?这不就是我们写完程序之后对程序进行调试嘛!
白盒测试主要通过检查软件内部的逻辑结构,对软件中的逻辑路径进行覆盖测试(调试的时候让一步一步地走);在程序不同地方设立检查点(调试过程中给程序打断点),检查程序的状态(调试过程中查看IDE中的监控窗口),确定实际运行状态与预期状态是否一致。
白盒测试的测试方法

白盒测试主要分为静态测试和动态测试两种。静态测试常见于桌面检查、代码审查、代码走查、代码扫描工具。而动态测试方法主要包含六种测试方法:语句覆盖、判定覆盖、条件覆盖、判定条件覆盖、条件组合覆盖、路径覆盖。静态测试前面我们已经说过了,下面我们就结合具体的案例来介绍动态测试的六种常见测试方法
语句覆盖
由于代码中会不可避免地存在大量的分支语句,因此我们在执行一次测试的时候,往往没办法让其跑完全部的代码。比如下面的switch case语句,如果1号测试用例输入的choice=1,那此次测试只能执行case 1对应的语句。
switch (choice) {
case 1:
System.out.println("执行操作 1: 保存数据");
break;
case 2:
System.out.println("执行操作 2: 读取数据");
break;
case 3:
System.out.println("执行操作 3: 更新信息");
break;
case 4:
System.out.println("执行操作 4: 删除记录");
break;
case 5:
System.out.println("执行操作 5: 打印报表");
break;
case 6:
System.out.println("执行操作 6: 导出数据");
break;
case 7:
System.out.println("执行操作 7: 导入文件");
break;
case 8:
System.out.println("执行操作 8: 显示帮助");
break;
case 9:
System.out.println("执行操作 9: 查看日志");
break;
case 10:
System.out.println("执行操作 10: 退出程序");
break;
default:
System.out.println("无效选择,请输入1-10之间的数字");
}
而我们作为测试人员,肯定是要把你程序中每个选项支都测一遍的。因此针对这种情况,我们会设计10个测试用例,依次让choice从1取到10。这样这10个测试用例测完之后,我们就把程序中所有语句都跑一遍了,我们也就实现了语句覆盖
到这里大家肯定就明白语句覆盖是啥意思了。其实就是测试人员通过调整每次测试的输入,从而确保测试用例全跑完时,程序中的每一句话都会被执行到(每句话至少执行一遍)。
如果测试用例全跑完时,程序中仍然存在某些语句一遍都没执行过,那我们就说你这个测试用例设计是有问题的,测试人员工作是不到位的,这个月底是要扣钱的
判定覆盖
判定覆盖的意思就是测试人员要确保程序中每个判定(如 if 语句、switch 分支、循环条件等)的 “真” 和 “假” 两种逻辑分支都被至少执行一次。
比如你看下面的语句
if (A > 5) {
action1();
}
如果我们要实现测试覆盖,我们就要有两个测试用例,一个用例A>5(比如取A=6),一个用例A<=5(比如A=5),这俩测试用例跑完,我们就说实现了对这段代码的判定覆盖
再看下一个例子假如说在你的代码中,有这么一段执行逻辑
if (A>5)
then action1
if (B>5)
then action2
每条if语句都有执行与不执行两种情况,这俩加一起就有四种情况
| if语句1 | if语句2 | |
|---|---|---|
| case1 | 执行 | 执行 |
| case2 | 不执行 | 执行 |
| case3 | 执行 | 不执行 |
| case4 | 不执行 | 不执行 |
那是不是我就要设计4个测试用例来测这四种情况呢?
其实不需要,因为不同条件语句之间相互独立,互不干涉。所以我们依然只需要两个测试用例,第一个测试用例:A=6且B=6,第二个测试用例:A=5且B=5。这俩用例测完之后,代码中每个分支语句的“真” 和 “假” 两种逻辑分支都会被执行一次,这其实就做到判定覆盖了(不信你自己回去看判定覆盖的定义)
这时候有人就说了,那你这判定覆盖,覆盖的也不咋彻底呀,别急,后面有覆盖彻底的方法
条件覆盖
条件覆盖的核心是确保程序中每个判定条件的“真”和“假”两种情况都被至少执行一次
有人看到这就想,感觉这条件覆盖的定义,读起来的意思和判定覆盖差不多呀。这俩看起来差不多,实际上还是有区别的,请看下面的例子
if (A > 5 && B < 10)
then action1
这个判定包含两个条件:A > 5 和 B < 10
如果我们要实现判断覆盖,可以设计如下的两个测试用例
- 1号用例:A=6 且 B=6 ,使得A > 5 && B < 10为真
- 2号用例:A=5 且 B=6,使得A > 5 && B < 10为假
而我们要实现条件覆盖,对应的测试用例要求
- 让
A > 5为真,A > 5为假各一次; - 让
B < 10为真,B < 10为假各一次。
这时候我们再用前面两个测试用例,就不行了。因为这两个测试用例执行完之后,B < 10 为假这种情况并没有出现
这个时候就可以用下面的一组测试用例:
- 用例1:
A=6,B=8→A>5真,B<10真; - 用例2:
A=3,B=12→A>5假,B<10假。
这样子就即可以实现这条语句的判定覆盖,也能实现这条语句的条件覆盖
看完上面的例子,我们来对条件覆盖做个总结
首先条件覆盖的覆盖程度比判定覆盖更细致,能验证每个条件的逻辑分支。那是不是说只要实现了条件覆盖,就一定能实现判定覆盖呢?
答案是不能,这就是条件覆盖的局限性,可能存在“条件都覆盖了,但判定的整体逻辑却未正确覆盖”的情况。例如判定 if (A || B),若使用下面这组测试用例:A真B假和A假B真,就可以实现条件覆盖,但仍然不能实现判断覆盖,因为这俩测试用例输入进去之后,这个if语句都是真的,if语句为假的情况未被覆盖
简言之,条件覆盖的目标是确保每个判定中的子条件都被“真”“假”验证,目的是更深入地排查条件逻辑中的漏洞。
判定条件覆盖
判定条件覆盖(Decision-Condition Coverage)结合了判定覆盖和条件覆盖的要求,核心是:
- 要求确保每个判定的“真”“假”分支至少执行一次(满足判定覆盖);
- 要求确保每个判定中的每个条件的“真”“假”情况至少执行一次(满足条件覆盖)。
读完之后是不是感觉实现这个要求有点难?其实我们刚刚举过的例子中,就有一个实现判定条件覆盖的案例
if (A > 5 && B < 10)
then action1
对于上面这段代码,使用下面的一组测试用例:
- 用例1:
A=6,B=8→A>5真,B<10真; - 用例2:
A=3,B=12→A>5假,B<10假。
这就可以同时满足两个要求
- 判定覆盖要求:
A > 5 && B < 10整体为“真”和“假”各一次; - 条件覆盖要求:
A > 5真/假各一次,B < 10真/假各一次。
此时我们就说这组测试用例实现了对上面那段代码的判定条件覆盖
判定条件覆盖比单独的判定覆盖或条件覆盖更严格,能同时验证判定分支和条件细节。 那是不是说我们设计的测试用例实现了判定条件覆盖,就是最优秀的设计呢?
也不是,判定条件覆盖仍有它的局限性,仍然可能遗漏某些条件组合。例如判定 if (A || B),若用例为 A真B真(判定真)和 A假B假(判定假),虽满足判定条件覆盖,但 A真B假 和 A假B真 这两种条件组合未被覆盖,可能隐藏逻辑漏洞。
条件组合覆盖
条件组合覆盖是白盒测试中逻辑覆盖方法中较为严格的一种,其核心要求是:设计足够多的测试用例,使得每个判定中的所有条件的可能取值组合都至少被执行一次,同时也能满足判定覆盖和条件覆盖的要求。
看完上面那段话,很多人肯定还是有点迷。下面我们就来看一个例子
if (A > 5 && B < 10)
then action1
仍然以包含两个条件的判定 if (A > 5 && B < 10) 为例:
该判定包含两个条件,每个条件有“真”(T)和“假”(F)两种状态,共存在 2×2=4种条件组合:
| A>5 | B<10 | A > 5 && B < 10 | |
|---|---|---|---|
| case1 | T | T | 真 |
| case2 | T | F | 假 |
| case3 | F | T | 假 |
| case4 | F | F | 假 |
条件组合覆盖要求这4种组合都必须被至少一个测试用例覆盖
下面就是一组实现了条件组合覆盖的测试用例
- 用例1:
A=6,B=8→ 覆盖组合1(T,T) - 用例2:
A=6,B=12→ 覆盖组合2(T,F) - 用例3:
A=3,B=8→ 覆盖组合3(F,T) - 用例4:
A=3,B=12→ 覆盖组合4(F,F)
条件组合覆盖就是逻辑覆盖方法中相当全面的一种,能覆盖所有条件的组合情况,比判定覆盖、条件覆盖、判定条件覆盖更严格。
由于条件组合覆盖的测试覆盖面最广,所以在测试的时候更容易发现因条件组合不当导致的逻辑错误。
但是条件组合覆盖也有他的缺点,当判定中的条件数量较多时(如n个条件),组合数会达到2n种,测试用例数量会急剧增加,设计这么庞大数量的测试用例组是不现实的,面对这样的情况,该怎么办呢?
这时候就要去求助万能的数学家了。数学家们给我们提供了一种叫做正交表的方法,使用这个表,就能在保证覆盖率的同时,尽可能减少测试用例的数量(具体细节前面我们介绍黑盒测试的正交表法时已经说过了,这里不再赘述)
路径覆盖
路径覆盖是白盒测试中逻辑覆盖方法中最全面的一种,其核心要求是:设计足够多的测试用例,使得程序中所有可能的执行路径都至少被执行一次。这里的“路径”指的是从程序入口到出口的一系列语句执行序列,由判定条件的分支选择形成不同路径。
下面我们以包含两个判定的代码片段为例:
if (A > 5) { // 判定1
action1();
} else {
action2();
}
if (B < 10) { // 判定2
action3();
} else {
action4();
}
这段代码存在 2×2=4条可能的执行路径:
- 判定1为真 → 执行action1 → 判定2为真 → 执行action3
- 判定1为真 → 执行action1 → 判定2为假 → 执行action4
- 判定1为假 → 执行action2 → 判定2为真 → 执行action3
- 判定1为假 → 执行action2 → 判定2为假 → 执行action4
路径覆盖要求设计4个测试用例,分别覆盖这4条路径,确保每条路径都被验证过。
看到这里,可能有人就会说,我咋感觉你这举的例子,和前面条件组合覆盖没啥区别呢。其实是有区别的,区别就在于:
- 条件组合覆盖的对象是一个if条件语句,它穷举的是if条件语句中更细的小条件的排列组合,比如
if(A&B),实现条件组合覆盖的测试用例就是(A真B真,A真B假,A假B真,A假B假) - 而路径覆盖的对象是一个程序,它穷举的是这个程序中各个if语句的排列组合,每种排列组合都对应着一种路线,执行完所有的路线,就实现了路线覆盖
路径覆盖是逻辑覆盖方法中最严格的一种,理论上能覆盖所有可能的执行流程,其他覆盖方法中所有可能遗漏的逻辑错误都会在路径覆盖中被排查出来
路径覆盖能最大限度地验证程序逻辑的完整性,尤其适合复杂分支结构的代码(如包含多个if-else、switch、循环的程序)。
即使如此,路径覆盖依然存在着我们老生常谈的缺陷,那就是很容易产生天文量级的测试用例数量
- 当程序中判定条件较多时,路径数量会呈指数级增长,导致测试用例数量过多,测试成本极高;
- 部分路径可能因逻辑约束无法实际执行(如“永假”条件导致的不可达路径),难以完全覆盖。
当然,我们依旧可以通过正交表法来减少测试用例的数量,从而一定程度上缓解路径覆盖的缺陷
黑盒测试
黑盒测试的概念
白盒测试是在你能看到测试对象内部结构的情况下进行的测试,那黑盒测试就是在你看不到测试对象内部结构情况下进行的测试
黑盒的意思就是,你只能看到这个系统与外界交互的接口,你可以通过接口往里面输入数据,然后通过界面看到系统的响应结果。至于它是怎么得出这个结果的,这个你是不知道的。
就像普通人用电脑一样,你就只知道,我把鼠标悬停在浏览器右上角的叉号上,然后点一下,这个窗口就关闭了。但你不知道你点击的那个动作,在计算机内部是如何导致窗口关闭的。那么计算机对于你来说,就是个黑盒。小时候家里买回来电脑之后,自己在那瞎捣鼓,其实相当于你在对计算机做黑盒测试
题外话:对于学过机组的同学,可能会对这个过程有一点了解,因为这个例子其实正好对应了计算机系统里“输入设备 → 驱动程序 → 操作系统 → 应用程序”的完整交互链。
下面是大致的过程描述
-
硬件层面
- 鼠标内部:机械/光学传感器检测到“左键按下”和“抬起”动作。
- USB/PS/2接口传输:鼠标把动作编码成数据包,通过接口发给主机。
- 中断触发:数据包到达时,触发CPU的外部中断请求(IRQ),通知系统有输入事件。
-
驱动程序层面
- 设备驱动:操作系统加载的鼠标驱动程序响应中断,读取数据包。
- 事件解析:驱动把原始数据解析成标准事件(如“左键按下”、鼠标坐标)。
- 上报到系统:驱动通过内核提供的接口(如Linux的input子系统、Windows的HID协议)把事件交给操作系统。
-
操作系统层面
- 事件队列:操作系统把事件放入输入事件队列(Event Queue)。
- 窗口管理器/桌面环境:
- 窗口管理器(如Windows Explorer、X Window Manager)从队列读取事件。
- 根据鼠标坐标判断事件发生在哪个窗口的哪个控件(这里是浏览器右上角的“关闭”按钮)。
- 消息分发:
- Windows:通过消息循环(Message Loop)把
WM_CLOSE消息发送给浏览器窗口的消息队列。 - Linux/X11:通过X Server把
ClientMessage或ButtonPress事件发送给浏览器进程。
- Windows:通过消息循环(Message Loop)把
-
浏览器应用层面
- 事件处理:
- 浏览器的GUI框架(如Chromium的UI框架)注册了“关闭按钮”的点击回调函数。
- 当收到关闭消息/事件时,执行回调。
- 关闭逻辑:
- 触发
window.close()或浏览器内部的“关闭标签页/窗口”函数。 - 执行清理操作:保存会话、释放内存、关闭网络连接等。
- 触发
- 退出窗口:
- 调用操作系统API(如Windows的
DestroyWindow,Linux的XDestroyWindow)请求关闭窗口。
- 调用操作系统API(如Windows的
- 事件处理:
-
返回到操作系统层面
- 窗口销毁:操作系统销毁窗口的图形资源(如窗口缓冲区、控件对象)。
- 资源回收:释放该窗口占用的系统资源(内存、句柄等)。
- UI刷新:重绘桌面或其他窗口,让被关闭窗口的区域消失。
黑盒测试的特点
黑盒测试是在完全不考虑程序逻辑和内部结构的情况下,检查系统功能是否按照需求规格说明书的规定正常使用、是否能适当的接收输入数据而输出正确的结果,满足规范需求。所以,黑盒测试又称之为数据驱动测试,不关心软件内部的实现逻辑,只注重软件的功能
黑盒测试的优点:
- 不需要了解程序内部的代码以及实现,不关注软件内部的实现。
- 从用户角度出发设计测试用例,很容易的知道用户会用到哪些功能,会遇到哪些问题,锻炼测试人员的产品思维
- 测试用例基于软件需求开发文档,不容易遗漏软件需求文档中需要测试的功能
黑盒测试的缺点:
- 由于黑盒测试不关注软件内部的实现,因此测试用例不可能覆盖所有代码。
黑盒测试的测试方法
黑盒测试用到的测试方法有,等价类,边界值,因果图,场景法,错误猜测法等。这个其实我们之前说过,为了方便阅读,这里就再介绍一遍
等价类
等价类分类:
- 有效等价类:对于程序的规格说明书是合理的、有意义的输入数据构成的集合,利用有效等价类验证程序是否实现了规格说明中所规定的功能和性能
- 无效等价类:根据需求说明书,不满足需求的集合。
举个例子:

缺点:等价类只考虑输入域的分类,没有考虑输入域的组合,需要其他的设计方法和补充。
边界值
边界值分析法就是对输入或输出的边界值进行测试的一种黑盒测试方法。边界值分析法通常作为对等价类划分法的补充,这种情况下,其测试用例来自等价类的边界。
边界值包含:边界值+次边界值
啥叫次边界值呢?看下面的例子
1)有效范围是[6,15]
边界值:6 15(有效)
次边界值:5 16(无效)
2)有效范围是(6,15)
边界值:6 15(无效)
次边界值:7 14(有效)
边界值即给定返回的左数据和右数据
选择次边界值的时候需要根据边界值的有效无效情况来定
- 若边界值为有效等价类中的数据,则次边界值为无效等价类中的边界
- 若边界值为无效等价类中的数据,则次边界值为有效等价类中的边界
综合考虑等价类和边界值,对应的测试用例可以这样设计

场景法
这个主要是站在用户的角度,模拟用户使用这个软件的一系列流程操作。以每一个执行流为一个测试用例。比如对于注册功能,我们先模拟出用户注册可能的几个执行流
- 点击注册时不同意协议
- 点击注册时同意协议了,但是填的信息格式不符合要求
- 点击注册时同意协议了,信息格式也对了,但注册的时候没网了
- 点击注册时同意协议了,信息格式也对了,但激活邮件发送失败了
- 成功发送激活邮件,但是24小时之内没有在激活邮件上点确认
确定基本流和备用流后,编写测试用例:
- 基本流:点击注册入口 同意协议,输入正确的信息,点击注册,成功激活
- 备用流:点击注册入口 不同意协议,重新点击注册入口 同意协议,输入正确的信息,点击注册,成功激活
- 备用流:点击注册入口 不同意协议,点击注册入口 不同意协议,输入错误的信息后重新输入正确的信息,点击注册,成功激活
在新活动上线的时候,我们还要确保新活动的上线对于老活动的正常运行没有影响。比如下面的例子中,如果把第一个if语句直接改成第二个if语句,那么5月份的活动就没有办法正常进行了,这就说明新活动的上线,对于老活动有影响,这就是不符合我们要求的。

正交表法
看下面的例子,按照我们一般人的测试思路,光是测试两种属性的不同组合。就需要设计4个测试用例。如果要测试五种属性的不同组合,需要设计32个不同的测试用例。那如果要设计10种属性的不同组合,我们可能就需要设计1000多个测试用例,这对于我们来说还是太繁琐了。

为了减少不同属性组合的测试用例数目,我们引入了正交表法。正交表就是通过数学的组合知识,在保证测试覆盖率的前提下,尽可能减少测试用例的数量,精简过之后形成的表格,相比全组合测试,正交表可减少70%-90%的用例量。
正交表满足下面的性质:
- 正交表的每列中不同数值出现的次数均等
- 在任意两列中,同一行的两个数值组成的有序数对出现的次数也是均等的
说了半天,你还是没告诉我正交表是咋来的呀,光告诉我他好,你不告诉我怎么做正交表,这不是糊弄人吗?
对于数学专业的同学来说,他们设计正交表肯定是小case,但是测试工程师,大部分都不是数学专业的,你让他们设计正交表,肯定有点难度。因此懂数学的程序员大佬们也开发出来了一款专门用于生成正交表的软件,你只需要输入相应的问题背景信息,运行可执行程序,它就能自动给你生成一份针对你给的问题的正交表。下面我们就来演示一下
(1)下载allpairs工具包

(2)在excel中输入正交表的因素和水平信息

(3)在allpairs.exe同级目录下创建一个txt文件

(4)选中excel表格中的输入信息,ctrl+c ctrl+v,粘贴到我们的txt文件中,然后直接保存


(5)打开命令行,在pair文件目录下输入指令allparis.exe test01.txt > res-test01.txt

(6)然后打开pair文件目录下的res-test01.txt,下面画红框的就是我们要的正交表

(7)根据正交表编写测试用例,同时补充遗漏的重要测试用例(下图中最后一条就是后面补充的)

有人会说,为啥要先在excel中写,然后再粘到txt文件中呢?你为啥不直接在txt文件中写呢?
主要是因为allpairs对txt文件格式要求非常严格,你自己手打,很容易打错。如果这么干,你执行exe文件时就会有下面的报错。

判定表法
什么是判定表呢?

下面举一个使用判定表法设计测试用例的实例,假如说我们注册账号时有如下规则:
用户输入的账号中包含admin字符,或者通过内部链接进入注册页面,提交注册按钮成为管理员身份;反之无管理员身份。
现在我们要根据这条规则设计测试用例,此时就可以用判定表法,具体的使用步骤如下
(1)找到有哪些输入和输出条件
输入:账户包含admin字符,内部链接进入注册页面,提交注册按钮
输出:管理员/无管理员

(2)找到输入和输出条件之间的关系
账号包含admin字符 || 内部链接进入注册页面 + 提交注册按钮 => 管理员
不包含admin字符 || 非内部链接进入注册页面 || 未点击注册按钮 => 无管理员身份
| 事件 | 记号 |
|---|---|
| 账号包含admin字符 | a事件 |
| 内部链接进入注册页面 | b事件 |
| 提交注册按钮 | c事件 |
| 管理员身份 | Y |
| 无管理员身份 | N |

(3)给出判定表

(4)根据判定表编写测试用例
a. 账号包含admin,非内部注册链接,点击注册按钮,为管理员身份
b. 账号包含admin,内部注册链接,不点击注册按钮,非管理员身份
c. 账号不包含admin,内部注册链接,点击注册按钮,为管理员身份
d. 账号包含admin,内部注册链接,点击注册按钮,为管理员身份
e. 账号包含admin,非内部注册链接,不点击注册按钮,非管理员身份
f. 账号不包含admin,非内部注册链接,点击注册按钮,非管理员身份
g. 账号不包含admin,非内部注册链接,不点击注册按钮,非管理员身份
错误猜测法
举个例子:张三要去卖瓜
用例1:因为张三这人不实诚,所以你要小心他缺斤少两
用例2:因为张三这人粗心,所以你要小心他的瓜被压坏了
用例3:因为张三这人小气,所以你要小心不要把他惹哭了
在上面这个例子中,我们会根据张三给人之前的印象,总结出一些打交道时需要注意的点(这也就是我们常说的刻板印象)。测试的时候也是一样,很多属性都会有一些在测试时需要值得特别注意的点。我们就可以基于这些刻板印象,设计一些基于错误猜测的测试用例
比如:
| 测试项 | 经常需要注意的点 |
|---|---|
| 密码 | 是否加密,是否具备安全性 |
| 获取用户输入 | 是否存在SQL注入的情况 |
| 软件存在多版本 | 多个版本都要测试 |
| 活动(每月存在且奖励不同) | 兼容前面月份的活动 |
灰盒测试
灰盒测试概念
灰盒测试(Gray-box Testing)是一种介于白盒测试和黑盒测试之间的软件测试方法,灰盒测试多用于集成测试阶段,不仅关注输出、输入的正确性,同时也关注程序内部的情况。
灰盒测试就很像是我们在调试的过程中进行的更宏观的测试,比如在下图 所示的代码中,我们默认BuildHttpResponse()与SendHttpResponse()这俩函数的功能已经通过单元测试检测完毕了,现在我们要检测这俩函数放一起顺序执行,能不能达到我们预期的效果

三种测试的能见度对比
下面是三种测试的能见度对比
- 白盒测试:知道内部代码结构、逻辑和实现细节,能看到“盒子”内部。
- 黑盒测试:完全不关心内部实现,只看输入和输出是否符合需求。
- 灰盒测试:知道部分内部结构或实现细节(比如接口定义、数据库结构),但不会深入到代码级别的逻辑。
灰盒测试举例
假设测试一个用户注册功能:
- 黑盒:输入用户名、密码,看是否注册成功
- 灰盒:除了输入输出验证,还会检查数据库中是否正确插入了新用户记录、字段长度是否符合定义
灰盒测试特别适合需要验证系统组件之间协作的场景,既能保持一定的黑盒独立性,又能利用部分内部知识提高测试效率。
灰盒测试能不能取代黑盒测试?
灰盒和黑盒测试都属于数据驱动测试(即给个输入,看输出是否符合预期),但是程序内部结构对黑盒测试是不可见的,对灰盒测试是可见的。既然如此,是不是说灰盒测试的效果比黑盒好呢?
但是,灰盒测试没有白盒测试详细和完整,黑盒测试是覆盖产品范围最广的测试,因此灰盒测试基本是不能够替代黑盒测试,否则需要很大的代价,设计非常多的用例。
常见面试题:你知道的测试方法有哪些?哪种用的比较多?
常见的测试方法有黑盒测试,白盒测试和灰盒测试。开发人员主要用白盒测试和灰盒测试,测试人员主要用白盒测试和黑盒测试。对于测试人员来说,相较于白盒测试,黑盒测试用的更多一些。
按照测试阶段分类
单元测试
单元测试的定义
所谓的单元测试,就是对你项目的最小组成单元进行白盒测试。那啥叫最小组成单元呢?举个例子
下面这个接口RecvHttpRequestLine(),它的功能就是用来将http请求报文从套接字的接受缓冲区中读到HttpRequest结构体中。
// 这个函数用于从接受缓冲区中读取请求行数据,将数据读到http_request.request_line中
bool RecvHttpRequestLine()
{
// 老版本
// Util::ReadLine(sock, http_request.request_line);
// http_request.request_line.resize(http_request.request_line.size()-1);
// LOG(INFO, http_request.request_line);
// 新版本
auto&line = http_request.request_line;
std::cout << http_request.request_line.size() << std::endl;
// 我们首先调用工具箱中的read line函数从socket接收缓冲区中。读取一行数据,这行数据就是HTTP请求报文的第一行。也就是请求行的数据。我们把它读到http_request的request_line中
// 调用ReadLine完成之后,Readline会将接受缓冲区中的一行数据读到 http_request.request_line中,Readline自身返回成功读取的字节数
// if(Util::ReadLine(sock, line) > 0 && line!="\n"){
if(Util::ReadLine(sock, line) > 0){
// std::cout<< int(line[0]) << std::endl;
line.resize(line.size() - 1);
LOG(INFO, http_request.request_line);
}
else{
// 走到这里说明读取出错,我们直接将stop置1返回
stop = true;
LOG(INFO,"请求行读取出错");
}
LOG(INFO,"stop = " + std::to_string(stop));
return stop;
}
注意看这个函数内部,没有嵌套其他的函数,这个接口可以看做是项目的最小组成单元。对这个接口的功能进行测试,就叫做单元测试
那最小单元有没有严格的定义呢?到底怎么才算“最小单元”呢?不用钻这个牛角尖。最小单元实际是人为定义的,一个方法,一个类都可以理解为“最小单元”,具体是什么,取决于你项目的架构
单元测试属于白盒测试还是黑盒测试?
单元测试就是你写完代码之后检查代码的运行逻辑是否符合你的要求,妥妥的白盒测试。
单元测试的参考依据
单元测试的目的是检测单元接口代码的运行逻辑是否符合预期。那这个预期是怎么定的呢?有可能是开发人员自己定的,也有可能是产品经理写在设计文档中的。如果是这个接口是开发人员设计的,那测试时候的依据就是开发时代码的注释。如果接口是产品经理设计的,那测试时候的依据就是项目设计文档
什么时候进行单元测试
根据前面的例子以及我们的开发经验,我们通常会写完一个接口之后,对其进行一次单元测试,检查这个接口的功能是否能够正常运行。
既然如此,单元测试是不是就是发生在单元接口写完之后呢?
实际上,单元测试不仅可以在单元接口写完之后进行,还可以在编码前进行,编码前进行测试的模式叫做测试驱动开发TDD
谁负责单元测试?
一般是谁写的这个单元接口,谁负责进行单元测试,一般是开发工程师~当然,某些重要的单元接口可能也会有专门的白盒测试工程师负责测试
单元测试的具体内容
接口测试
首先我们要测这个接口的功能是否符合预期
- 测试正常输入能否给出正常结果
- 测试当输入值为条件边界值时,接口能否给出预期的正确结果
- 对异常的输入,测试接口能否做适当的处理
- 例如,当传入非法参数、资源不足、依赖服务异常时,模块是否能按照预期进行错误捕获、提示或回滚操作。
- 比如测试一个文件操作方法,当文件不存在时,是否能抛出正确的异常或返回合理的错误码。
局部数据结构测试
对模块内部的某个重要的数据结构进行检验,检测对象的定义、初始化、类型、作用域等是否符合预期。
例如检查数组是否越界、变量是否在使用前被正确初始化、结构体成员是否正确赋值等,确保数据结构在模块内部的操作中是合法且稳定的。
路径测试
测试用例覆盖该单元的所有执行路径,确保每一条可能的代码执行路径都能按照我们预期的那样去执行,避免因路径覆盖不全导致的逻辑漏洞
集成测试
看下面这个接口RecvHttpRequest(),它的功能就是用来将http请求报文的的请求行和报头从套接字的接受缓冲区中读到HttpRequest结构体中
// 这个函数负责http请求报文的接收和解析
// 接收指的是将http请求报文从套接字的接受缓冲区中读到内存中
// 解析是指将报文中的各字段提取出来,写入HttpRequest结构体的各成员变量中
void RecvHttpRequest()
{
// RecvHttpRequestLine与RecvHttpRequestHeader两个函数返回的结果分别表示在这两次读取过程中,有没有出现读取失败的情况
// 如果返回值为false,说明中间读取非常顺利
if((!RecvHttpRequestLine()) && (!RecvHttpRequestHeader())){
// 如果这俩函数的读取都非常顺利,我们就进一步去解析它们读上来的数据
ParseHttpRequestLine();
ParseHttpRequestHeader();
// 这俩函数执行完之后,我们就将一份http报文的请求行和请求报头从接收缓冲区读到了http_request对象的requestline和requestheader中
RecvHttpRequestBody();
// 这个函数再执行完之后,如果这个请求有正文部分,我们就把这个请求的正文部分读到了http_request.body中
}
}
通过观察我们发现,这个接口内部又嵌套了若干的基本功能接口。之前我们在写这些基本接口的时候,已经顺带完成了对这些接口的单元测试。即我现在可以确保,这些接口内部是没问题的,我现在想测的是,这些接口按照我代码中写的那样组合起来,能不能按照我预期的那样协同运行,最终实现RecvHttpRequest()的预期功能。对RecvHttpRequest()接口的测试,就可以看做是一个集成测试
集成测试也称联合测试(联调)、组装测试,将程序模块采用适当的集成策略组装起来,对系统的接口及集成后的功能进行正确性检测的测试工作。集成主要目的是检查软件单位之间的接口是否正确。
集成测试属于白盒测试还是黑盒测试?
集成测试先要确保程序内部的各接口能够正常协调运行,然后进一步确保正确的输入可以得到正确的输出,异常的输入可以得到合适的处理。
集成测试不仅关注输出、输入的正确性,同时也关注程序内部的情况。这是典型的灰盒测试
集成测试的参考依据
通常集成测试的对象也是一个功能接口,只是这个功能接口内部比较复杂而已。如果是这个接口是开发人员设计的,那集成测试的依据就是开发时代码的注释。如果接口是产品经理设计的,那集成测试的依据就是经理给出的项目设计文档
什么时候进行集成测试
等到内部的接口都完成了单元测试,我们就需要对外面的接口进行集成测试了
谁负责集成测试?
要么是开发这个接口的人,要么是专门的集成测试工程师
集成测试的具体内容
测试内容:模块之间数据传输、模块之间功能冲突、模块组装功能正确性、全局数据结构、单模块缺陷对系统的影响等等
不管是集成测试还是单元测试,它的测试对象大概率都是一个接口。所以具体内容基本上都是接口测试那一套
系统测试
系统测试中的系统,其实指的就是你这个开发的项目。该项目的所有软件和硬件组成了一个系统,你测试测的就是这个系统。
系统测试属于黑盒测试还是白盒测试?
到了系统测试的时候,基本上你这个项目就已经开发的差不多了,系统测试要求测试人对产品进行系统层面的测试。
当你站在东方明珠的顶部往下看,你肯定是看不到地面上某个人穿的是啥鞋的。同理在系统这个宏观的层面,你再去打磨项目的细节,已经不现实了,因此我们只能对系统进行黑盒测试。系统测试就是典型的黑盒测试,由专门的黑盒测试工程师负责完成
系统测试的测试内容
其实就是黑盒测试的测试内容,即对你这个产品进行功能测试、界面测试、可靠性测试、易用性测试、性能测试、兼容性测试、安全性测试
冒烟测试
什么是冒烟测试
举两个生活中的例子,
- 比如我们在商场买了一台电视,电视运回家之后,我们首先会通电,查看电视是否能够运行。
- 比如我们在商场买了一个水杯,拿回家之后,我们首先会往杯子里面灌水,查看水杯是否漏水。
在上面俩例子中,我们把东西买回家之后干的第一件事情——给电视通电、往杯子里灌水,其实就是在做冒烟测试。
在工作中,假如有一个博客系统项目提测了,测试人员拿到手干的第一件事情,肯定是要看看这个测试系统是否能够成功打开,主流程是否可以走通,这其实就是在做冒烟测试
冒烟测试的对象是一个开发好的完整的系统(这个系统刚刚开发好,目前还没有进行系统测试)。冒烟测试的目的就是先检测一下你这个系统能不能正常跑起来
更正规一点的说法,冒烟测试的对象是每一个新编译的需要正式测试的软件版本,目的是确认软件主要功能和核心流程正常
冒烟测试名字的由来
冒烟这一术语源自硬件行业。当硬件工程师对一个硬件或硬件组件进行更改或修复后,首先会在开发板上进行冒烟测试,即直接给板子加电,如果开发板没有冒烟,能够正常运行,说明当前设计的电路没有大问题,可以实现基本功能。确定主体功能没啥问题,硬件工程师才能进一步地细化,进行更完善的测试。
这个其实就和我们解决问题的一般思路是一样的,当你遇到一个问题之后,你肯定是先要想出解题的大致思路(解题框架),然后具体执行的时候再一步步细化。冒烟测试的目的,就是先检测你这个系统到底能不能正常跑起来,要是跑都跑不起来,那还测个p啊。
什么时候进行冒烟测试?
冒烟测试的时机位于系统开发完毕之后,对这个系统进行系统测试之前。
开发人员把系统开发完毕之后,会将这个系统交给测试人员进行提测。测试人员拿到手之后,会先对该系统进行冒烟测试。如果冒烟测试通过,则开始进行正式的系统测试,如果不通过,则测试人员会将该系统打回,让开发人员重新修复代码,直到冒烟测试通过
回归测试
随着一个互联网公司越做越大,这个公司的产品功能肯定也会越来越强大。产品每次更新,肯定都会推出一些新的功能,这些新功能肯定是要先经过完整的测试,确保没问题了才会上线。但是在产品中也有这样的一类功能,这些功能以前就有,未来也会继续存在下去。
对于这样的功能,它们的相关接口久经历史考验,虽然可能前面出现过很多问题,但随着工程师不断完善,功能越来越稳定,在现在的版本中几乎已经不会出错了。那请问在新版本更新前,测试人员还需不需要对这些老接口进行测试呢?
有人肯定会说,测试这玩意那肯定是多多益善啊,能测就多测测。道理是这样,但是对于一个主要工作是点点点的测试人员来说,一个久经考验的功能,一定会积累非常多的测试用例。毕竟是枯燥的重复劳动,你让他每次都重新测一遍,他肯定是不太愿意的,测试人员的立场肯定是能不测就不测。
那现在假如说你是这个项目的经理,从项目的角度整体考虑,请问在新版本更新前,究竟需不需要让测试人员对这些老接口进行测试呢?
答案是依旧需要!原因是项目是一个有机的整体,各部分之间都存在着千丝万缕的联系,有些联系我们是知道的,但还有很多联系是我们未察觉的。对于这样的项目,往往一个小小的修改就有可能牵一发而动全身。正是因为项目中存在着很多我们未察觉到的联系,我们没法保证老功能完全不会受到新修改的代码的影响。因此当软件被修改后,我们不仅要检查修改的部分是否正确,还必须确认之前正常的功能没有因为这次修改而出现异常。
上面所描述的,检测新版本中老接口的功能是否正常的测试,就是回归测试。回归测试的目的就是确保在新版本中老功能依然能够正常工作。
更具体一点来说,回归测试的核心目标就是:
- 验证已修复的缺陷是否真正解决,且没有复现
- 确保新的代码修改没有对现有功能产生负面影响
- 保障软件的稳定性和可靠性,避免“改了一个问题,冒出多个新问题”
例如:一个购物APP修复了支付功能的bug后,回归测试不仅要确认支付功能正常,还要检查商品浏览、加入购物车、订单管理等原有功能是否依然可用。
好的,前面我们论述了回归测试的必要性,产品经理下达回归测试的命令之后,最苦逼的就是测试人员,因为手里的活更多了,只有测试人员受伤的世界达成了。
那测试人员有没有办法救一下呢?为了拯救测试人员的假期,测试工程师就引入了自动化测试的技术,大大提高了回归测试的效率。
回归测试非常适合用自动化测试来测,为啥呢?
前面我们说了,回归测试的对象都是那些产品中久经考验的老接口。这样的接口功能基本上不会再变了,因此它的测试流程与测试用例虽然比较复杂,但往往是固定的,前辈早就给你设计好了,就不用你自己费事设计了,你只需要按照文档一步一步做就行了。而固定的流程,自然是最适合自动化测试了
验收测试
什么是验收测试
假如说公司开发的一款软件已经通过了测试人员的系统测试,即这款软件已经较好地实现了计划书中规定的各项功能,这是不是就意味着这款软件就能上线了呢?
很多时候我们的项目组扮演的是一个乙方的角色,即甲方给我们提用户需求,我们将用户需求转化成软件需求,然后一步步实现。我们产品的最终目的,是要让用户用的满意,产品好不好,我们说了不算,只有用户说了算。因此当软件完成了公司内部的系统测试之后,下一步就是交给甲方验收。验收验收,就是要先验再收。甲方在收货之前对产品进行的检验,就叫做验收测试。
验收测试是部署软件之前的最后一个测试操作。它是技术测试的最后一个阶段,也称为交付测试。其目的是确保软件准备就绪,按照项目合同、任务书、双方约定的验收依据文档,向软件购买方展示该软件系统满足原始需求。
什么时候进行验收测试?
产品的系统测试通过之后
验收测试的对象是什么?谁来验收?
- 测试对象:整个系统(包括软硬件)。
- 测试人员:主要是最终用户或者需求方。
验收测试的依据是什么?
- 测试依据:用户需求、行业验收标准
验收测试是黑盒测试还是白盒测试?
- 黑盒测试
验收测试和系统测试的区别
验收测试由甲方来测,系统测试由乙方来测。除了这点区别之外,验收测试和系统测试的其他指标几乎都是一样的
按照是否手工测试
手工测试
点点点,没啥好说的
自动化测试
通过脚本控制机器帮我们点点,具体又可以分为接口自动化测试和UI自动化测试,后面我们单独出一节会说这个事儿
下面是这俩测试的简单对比,很容易理解,就不细说了
| 自动化测试 | 手工测试 | |
|---|---|---|
| 优点 | 节省成本 提高测试人员执行工作效率 保障软件的质量 | 对测试人员技术要求没有自动化技术要求高 可以进行发散性测试 |
| 缺点 | 对测试人员技术要求较高 不能发散性测试 | 效率低 人员、时间成本比起自动化测试都比较高 |
按照测试人员的身份划分
α测试(Alpha Testing)
α测试又叫内测或者叫a测,其实都是一个涵义,通常就是指公司内部账号在模拟实际操作环境下进行的测试。
α测试的目的是评价软件产品的FLURPS(即功能、可使用性、可靠性、性能和支持)。α测试不能由程序员或测试员完成。
β测试(Beta Testing)
β测试又叫公测或者叫b测,说道公测,这个玩游戏的hxd肯定是太熟了。游戏公司经常会发送一些邀请码,来邀请用户参与游戏公测。被邀请的玩家们可以在任意的时间来上号玩游戏,如果发现问题,就及时告诉公司。
之所以要有公测,主要还是因为内测用的是模拟的实际环境,跟真实的环境比肯定还是有很大差别的,光靠内测发现问题太低效了。
α测试与β测试的区别
| 区别点 | 说明 |
|---|---|
| 测试的场所不同 | α测试是在公司内部进行测试的,但是β测试是在用户环境下进行测试的 α测试的环境是受开发方控制的,用户的数量相对比较少,时间比较集中 β测试的环境是不受开发方控制的,用户数量相对比较多,时间不集中 |
| 测试执行时机不同 | 通常是α测试通过后,在进行β测试 |
| 测试持续时间长短不同 | α测试时间没有β测试持续时间长 |
第三方测试
如果公司没啥测试经验,也可以将产品交给专门的第三方测试公司来测
按照国内国外划分
本地测试
前面说的所有测试,都默认是本地测试,就是在国内环境下的测试
国际化测试
测试人员在进行国际化测试的过程中,其实就是要关注:
- 布局
- 时间
- 日期
- ……
- 数字格式
- 货币
- 机器型号
- ……
这些国际化相关属性符不符合标准

被折叠的 条评论
为什么被折叠?



