软件测试 52 讲¶
茹炳晟。从 2002 年开始做软件开发,2005 年时转型成为测试工程师。现任腾讯 TEG 基础架构部 T4 级专家,曾在 Ebay 任职。
01测试基础知识篇 (11讲)¶
02 _ 如何设计一个“好的”测试用例¶
最常用的黑盒测试方法:
1. 等价类划分方法
2. 边界值分析方法
3. 错误推测法
备注
测试真正目标不是保证全面覆盖,而是追求在有限的时间资源以及人力成本资源的情况下,寻找质量风险和测试成本之间的平衡点。
“好的” 测试用例必须具备哪些特征:
1. 整体完备性: “好的” 测试用例一定是一个完备的整体,是有效测试用例组成的集合,能够完全覆盖测试需求。
2. 等价类划分的准确性: 指的是对于每个等价类都能保证只要其中一个输入测试通过,其他输入也一定测试通过。
3. 等价类集合的完备性: 需要保证所有可能的边界值和边界条件都已经正确识别。
03 _ 什么是单元测试¶
Mock 代码和桩代码的本质区别是:测试期待结果的验证(Assert and Expectiation):
对于 Mock 代码来说,我们的关注点是 Mock 方法有没有被调用,
以什么样的参数被调用,被调用的次数,以及多个 Mock 函数的先后调用顺序。
所以,在使用 Mock 代码的测试中,对于结果的验证(也就是 assert),通常出现在 Mock 函数中。
对于桩代码来说,我们的关注点是利用 Stub 来控制被测函数的执行路径,不会去关注 Stub 是否被调用以及怎么样被调用。
所以,你在使用 Stub 的测试中,对于结果的验证(也就是 assert),通常出现在驱动代码中。
通俗来讲:
如果你的测试验证是在被测函数中进行的,那么此时你使用的就是桩函数;
如果你的测试验证是在被模拟的函数中进行的,那么这个被模拟的函数就是 Mock 函数。
05 自动化测试¶
什么样的项目适合自动化测试:
第一,需求稳定,不会频繁变更。
第二,研发和维护周期长,需要频繁执行回归测试。
第三,需要在多种平台上重复运行相同测试的场景。
第四,某些测试项目通过手工测试无法实现,或者手工成本太高。
06 _ 测试覆盖率¶
需求覆盖率统计方法属于传统瀑布模型下的软件工程实践,传统瀑布模型追求自上而下地制定计划、分析需求、设计软件、编写代码、测试和运维等,在流程上是重量级的,已经很难适应当今互联网时代下的敏捷开发实践。现在人们口中的测试覆盖率,通常默认指代码覆盖率,而不是需求覆盖率。
行覆盖率又称为语句覆盖率,指已经被执行到的语句占总可执行语句(不包含类似 C++ 的头文件声明、代码注释、空行等等)的百分比。这是最常用也是要求最低的覆盖率指标。
判定覆盖又称分支覆盖,用以度量程序中每一个判定的分支是否都被测试到了,即代码中每个判断的取真分支和取假分支是否各被覆盖至少各一次。比如,对于 if (a>0 && b>0),就要求覆盖 “a>0 && b>0” 为 TURE 和 FALSE 各一次。
条件覆盖是指,判定中的每个条件的可能取值至少满足一次,度量判定中的每个条件的结果 TRUE 和 FALSE 是否都被测试到了。比如,对于 if (a>0 && b>0),就要求 “a>0” 取 TRUE 和 FALSE 各一次,同时要求 “b>0” 取 TRUE 和 FALSE 各一次。
07 _ 软件缺陷报告¶
缺陷报告是测试工程师与开发工程师交流沟通的重要桥梁,也是测试工程师日常工作的重要输出。
09 _ 软件测试工程师的核心竞争力¶
测试工程师要具备的七项核心竞争力,包括:
1. 测试策略设计能力
最核心的竞争力,也是最难培养的
一定是需要你在大量实践的基础上潜移默化形成的
关键问题:
测试要具体执行到什么程度;
测试需要借助于什么工具;
如何运用自动化测试以及自动化测试框架,以及如何选型;
测试人员资源如何合理分配;
测试进度如何安排;
测试风险如何应对。
2. 测试用例设计能力
多积累,对常见的缺陷模式、典型的错误类型以及遇到过的缺陷,要不断地总结、归纳,才能逐渐形成体系化的用例设计思维
阅读一些好的测试用例设计实例开阔思路
3. 快速学习能力
对不同业务需求和功能的快速学习与理解能力;
对于测试新技术和新方法的学习与应用能力。
4. 探索性测试思维
在执行测试的过程中不断学习被测系统,同时结合基于自己经验的错误猜测和逻辑推理,整理和分析出更多的有针对性的测试关注点
5. 缺陷分析能力
对已经发现的缺陷,结合发生错误的上下文以及后台日志,可以预测或者定位缺陷的发生原因
根据已经发现的缺陷,结合探索性测试思维,推断同类缺陷存在的可能性,并由此找出相关的潜在缺陷
对一段时间内所发生的缺陷类型和趋势进行合理分析,由点到面预估整体质量的健康状态,并能够对高频缺陷类型提供系统性的发现和预防措施,并以此来调整后续的测试策略
6. 自动化测试技术
7. 良好的沟通能力
测试开发工程师的核心竞争力:
第一项核心竞争力,测试系统需求分析能力
第二项核心竞争力,更宽广的知识体系
性能测试工程师的核心价值:
是对于性能问题的直觉和定位能力
有核心竞争力的性能测试工程师就像经验丰富的医生
11 _ 互联网产品的测试策略¶
通常情况下,互联网产品要求全回归测试的执行时间不能超过 4 小时。
金字塔模型是适合传统软件产品的测试策略
互联网产品适合棱形模型的原因是在于互联网产品的 “快”,快速实现功能,快速寻求用户反馈,快速试错,快速迭代更新。
总结来讲,互联网产品的全面单元测试只会应用在那些相对稳定和最核心的模块和服务上,而应用层或者上层业务服务很少会大规模开展单元测试。
02GUI自动化测试篇 (10讲)¶
15 自动化过程中的测试数据¶
从创建的技术手段上来讲,创建测试数据的方法主要分为三种:
1. API 调用
好处:
测试数据的准确性直接由产品 API 保证
坏处:
并不是所有的测试数据都有相关的 API 来支持
API 调用方式的执行效率低
2. 数据库操作
缺点:
数据库表操作的任何变更,都必须同步更新测试数据工具中的 SQL 语句
3. 综合运用 API 调用和数据库操作
从创建的时机来讲,测试数据创建的方法主要分为两种:
Out-of-box: 测试用例执行前,事先创建好 “开箱即用” 的测试数据
对于相对稳定的测试数据,比如商品类型、图书类型等,往往采用 Out-of-box 的方式以提高效率
On-the-fly: 测试用例执行过程中,实时创建测试数据
对于那些只能一次性使用的测试数据,比如商品、订单、优惠券等,往往采用 On-the-fly 的方式以保证不存在脏数据问题
03API自动化测试篇 (3讲)¶
22 _ 常用API测试工具简介¶
API 测试的基本步骤:
1. 准备测试数据(这是可选步骤,不一定所有 API 测试都需要这一步)
2. 通过 API 测试工具,发起对被测 API 的 request
3. 验证返回结果的 response
[复杂业务]被测业务操作是由多个 API 调用协作完成:
通过类似于 Fiddler 之类的网络抓包工具,获取这个调用序列
基于用户行为日志,通过大数据手段来获取这个序列
23 _ API自动化测试框架的前世今生¶
早期的基于 Postman 的 API 测试:只能做简单测试
基于 Postman 和 Newman 的 API 测试:可以和CI/CD 流水线的整合
基于代码的 API 测试:可以处理连续调用多个 API 并且有参数传递的情况
自动生成 API 测试代码:解决重复工作量的问题
Response 结果发生变化时的自动识别:内建数据库,记录每次调用的 request 和 response 的组合,当下次发送相同 request 时,API 测试框架就会自动和上次的 response 做差异检测,对于有变化的字段给出告警。
24 _ 微服务模式下API测试要怎么做¶
04代码测试篇 (3讲)¶
25 _ 掌握代码级测试的基本理念与方法¶
代码级测试常用方法:
静态方法:
在不实际执行代码的基础上发现代码缺陷的方法
动态方法:
通过实际执行代码发现代码中潜在缺陷的方法
05性能测试篇 (7讲)¶
软件性能通常会包含:
1. 算法设计
2. 架构设计
3. 性能最佳实践
4. 数据库相关
5. 软件性能的可测试性
衡量软件性能的三个最常用的指标:
1. 并发用户数
2. 响应时间
3. 系统吞吐量
性能测试方法分为七大类:
1. 后端性能测试(Back-end Performance Test)
2. 前端性能测试(Front-end Performance Test)
3. 代码级性能测试(Code-level Performance Test)
4. 压力测试(Load/Stress Test)
5. 配置测试(Configuration Test)
6. 并发测试(Concurrence Test)
7. 可靠性测试(Reliability Test)
后端性能测试的场景设计主要包括以下两种方式:
1. 基于性能需求目标的测试验证;
2. 探索系统的容量,并验证系统容量的可扩展性
3. 压力测试:在临界饱和状态的基础上继续施加压力,直至系统完全瘫痪,观察这个期间系统的行为;然后,逐渐减小压力,观察瘫痪的系统是否可以自愈。
性能测试的四大应用领域:
1. 能力验证
2. 能力规划
3. 性能调优
4. 缺陷发现
后端性能测试工具的原理:
1. 虚拟用户脚本生成器
如 LoadRunner 是通过录制后再修改的方式生成虚拟用户脚本
而 JMeter 主要是通过添加各种组件,然后对组件进行配置的方式生成虚拟用户脚本
2. 压力产生器
实际发起测试负载的机器称为压力产生器
3. 压力控制器
有了多台压力产生器,那就需要一个专门的控制器来统一管理与协调这些压力产生器
4. 系统监控器
完成监控和数据收集的模块
5. 测试结果分析器
将系统监控器收集的所有信息汇总为完整测试报告
基于该报告生成各类指标的各种图表,还能将多个指标关联在一起进行综合分析来找出各个指标之间的关联性
四种测试类型:
1. 性能基准测试
注意:不要使用过大的测试负载,因为测试负载过大的话,系统资源也会成为性能瓶颈
对每个预发布版本的性能基准测试,我们可以保证新发布系统的整体性能不会下降
2. 稳定性测试
又称可靠性测试,主要是通过长时间(7\*24 小时)模拟被测系统的测试负载,来观察系统在长期运行过程中是否有潜在的问题
一般是采用 “波浪式” 的测试负载,比如先逐渐加大测试负载,在高负载情况下持续 10 多个小时,然后再逐渐降低负载
这样就构成了一个 “波浪”,整个稳定性测试将由很多个这样的波浪连续组成。
稳定性测试成功完成的标志,主要有以下三项:
a. 系统资源的所有监控指标不存在 “不可逆转” 的上升趋势
b. 事务的响应时间不存在逐渐变慢的趋势
c. 事务的错误率不超过 1%
实际工程项目中,由于稳定性测试执行的时间成本很高,往往需要花费 3~7 天的时间,
所以我们一般是在其他所有测试都已经完成,并且所有问题都已经修复之后才开始稳定性测试。
3. 并发测试
在高并发情况下验证单一业务功能的正确性以及性能的测试手段
作为功能测试的补充,主要用于发现诸如多线程、资源竞争、资源死锁之类的错误
4. 容量规划测试
主要目的是,解决当系统负载将要达到极限处理能力时
如何通过垂直扩展(增加单机的硬件资源)和水平扩展(增加集群中的机器数量)增加系统整体的负载处理能力的问题。
备注
全链路压测,是基于真实的生产环境来模拟海量的并发用户请求和数据,对整个业务链路进行压力测试,试图找到所有潜在性能瓶颈点并持续优化的实践。全链路压测的应用场景,不仅仅包括验证系统在峰值期间的稳定性,还会包含新系统上线后的性能瓶颈定位以及站点容量的精准规划。
全链路压测需要解决最重要的四个技术难点:
1. 海量并发请求的发起
2. 全链路压测流量的隔离
3. 实际业务负载的模拟
4. 测试完成后的数据清理
06测试数据准备篇 (4讲)¶
原始手段:
将测试数据准备的相关操作封装成数据准备函数:
1. 使用暴露全部参数的数据准备函数
例: User user=createUser(“TestUser001”, “abcdefg1234”, userType, paymentDetail, country, true);
2. 专用封装函数
例:
createDefaultUser() {
createUser(A, B, C, D, E)
}
createXXXUser(AX) {
createUser(AX, B, C, D, E)
}
createYYYUser(AY, BY) {
createUser(AY, BY, C, D, E)
}
3. 生成器模式
默认: UserBuilder.build()
指定XXX: UserBuilder.WithXXX(AX).build()
“死水数据” 适合用 Out-of-box 的方式
“活水数据” 适合采用 On-the-fly 的方式
统一测试数据平台:
服务化了数据准备函数的功能,并且提供了 GUI 界面以方便用户使用
07测试基础架构篇 (4讲)¶
Selenium Grid 是目前最常用也是主流的测试分布式执行架构
Selenium1/2/3是gui自动化框架,1的原理和2和3的差别较大
selenium有三大组件,SeleniumIDE、Selenium WebDriver、Selenoium Grid
08测试新技术篇 (5讲)¶
43 探索性测试¶
探索式测试,最早是由测试专家 Cem Kaner 博士在 1983 年提出的,并受到当时语境驱动的软件测试学派的支持。后来,Cem Kaner 博士在佛罗里达工学院的同事 James A. Whittaker,凭借着在微软和谷歌担任测试架构师和测试总监的经验积累,撰写了最早的探索式测试书籍(Exploratory Software Testing),扩展了探索式测试的概念和方法。
备注
Exploratory software testing is a style of software testing that emphasizes the personal freedom and responsibility of the individual tester to continually optimize the value of her work by treating test-related learning, test design, test execution, and test result interpretation as mutually supportive activities that run in parallel throughout the project.
首先,探索式测试是一种软件测试风格,而不是一种具体的软件测试技术
其次,探索式测试强调独立测试工程师的个人自由和责任,其目的是为了持续优化其工作的价值
最后,探索式测试建议在整个项目过程中,将测试相关学习、测试设计、测试执行和测试结果解读作为相互支持的活动,并行执行
探索式测试相比即兴测试更强调及时 “反馈” 的重要性。在探索式测试中,测试工程师不断提出假设,通过测试执行去检验假设,通过解读测试结果证实或推翻假设。在这个迭代过程中,测试工程师不断完善头脑中被测试应用的知识体系,并建立被测应用的模型,然后利用模型、过往经验,以及测试技术驱动进一步的测试。探索式测试要不停地优化测试模型和测试设计,探索式测试有明确的测试目标和测试设计,只是测试设计的时间很短,会以很高的频率与测试执行交替切换。
把测试人员比喻作 client、把被测对象比作 server,这时就可以重新定义 “测试”—— 是测试人员和被测对象之间的一次对话(session),不断质疑系统。——软件测试的批判性思维: https://mp.weixin.qq.com/s/SsJyUkIbAkriTaI_CpWJoQ
高效开展探索式测试的前提是,对被测系统的设计以及行业应用有非常清晰的认识,同时在此基础上以发散的方式对系统可能存在的缺陷进行探索。所以,这就要求测试人员不仅要具有很深的业务领域知识,还需要很强的逻辑推理和分析能力。而这样的人才,属于比较稀缺的。另外,探索式测试不要到了项目后期再集中展开,而是应该在各个模块级别就尽可能多地去探索,尽量在测试早期就能发现问题。
需要注意的是,一定不要在执行层面,将探索式测试变成了随机测试,你设计的所有后续测试步骤都必须是在你之前的步骤上推演出来的。而且,在执行探索性测试的过程中,你需要明确每个操作的目的是什么,是想证实自己的推论还是要推翻自己的假设。对此,你一定要做到心中有数,否则很容易就会变成无明确目的随机测试。
而从管理层的角度来看,千万不要以探索式测试发现的缺陷数量来考核团队的绩效。因为这样不仅不能提升测试效率,反而会把大量的时间浪费在一些非核心功能的测试上。
44 测试驱动开发(TDD)¶
备注
TDD 并不是一门技术,而是一种开发理念。它的核心思想,是在开发人员实现功能代码前,先设计好测试用例的代码,然后再根据测试用例的代码编写产品的功能代码,最终目的是让开发前设计的测试用例代码都能够顺利执行通过。
TDD 的优势:
1. 保证开发的功能一定是符合实际需求的。
2. 更加灵活的迭代方式。
3. 保证系统的可扩展性。
4. 更好的质量保证。
5. 测试用例即文档。
45 精准测试¶
备注
概念在 2016 年被提出来。所谓精准测试,就是借助一定的技术手段、通过算法的辅助对传统软件测试过程进行可视化、分析以及优化的过程。也就是说,精准测试可以使得测试过程可视、智能、可信和精准。
核心思想是,借助一些高效的算法和工具,收集、可视化并且分析原生的测试数据,从而建立起一套测试分析系统。
46 渗透测试¶
渗透测试的常用方法:
1. 有针对性的测试
“开灯” 测试
2. 外部测试
3. 内部测试
4. 盲测
盲测是由专业渗透测试团队在测试后期开展的,通常会借助很多黑客攻击工具
5. 双盲测试
也叫作 “隐秘测试”
被测系统内部也只有极少数人知道正在进行安全测试
双盲测试可以用于测试系统以及组织的安全监控和事故识别能力,及其响应过程
执行渗透测试的步骤:
第一步:规划和侦察
第二步:安全扫描
静态分析阶段: Fortify SCA 和 Checkmarx Suite
动态分析阶段: 可以实时提供应用程序的运行时视图
第三步:获取访问权限
第四步:维持访问权限
第五步:入侵分析
渗透测试的常用工具:
Nmap
Aircrack-ng
sqlmap
Wifiphisher
AppScan
47 _ 用机器设计测试用例: 基于模型的测试¶
备注
MBT 的基本原理是通过建立被测系统的设计模型,然后结合不同的算法和策略来遍历该模型,以此生成测试用例的设计。
常用的模型:
有限状态机
状态图
UML
常见的 MBT 工具:
BPM-X
fMBT
GraphWalker
09测试人员的互联网架构核心知识篇 (5讲)¶
讲的比较浅,可以忽略不看
10特别放送篇 (8讲)¶
eBay 是全球知名的大型电子商务网站,已经大规模使用了微服务架构。对于单元测试而言,eBay 并不会对所有的微服务以及所有的中间件全面开展单元测试。我们只会选取一些偏底层的核心应用来全面开展单元测试,而对于产品的前端代码、偏业务应用的代码,很少会执行完整意义上的单元测试。
代码覆盖率的挑战并不是来自于技术本身,而是来自于管理。
性能需求的获取是一个关键且困难的环节:
类似“系统最大支持 500 万用户同时在线”的需求,这同样是一个看似具体,实则非常笼统的性能需求
500 万个用户都在执行查询操作和这 500 万个用户什么不做,对后端服务器的压力是天壤之别的
这个需求获取的难点就是,要能够准确估算这 500 万在线用户执行的各种类型的业务操作所占的百分比
很多的性能需求并不是直接从产品经理那里获取的,
而是需要资深的性能测试人员根据以往的经验,以及同类系统和竞品的业务流量来自己估算
性能测试:
1. 对于关键业务代码以及中间件代码的核心算法部分,基于时间复杂度和空间复杂度的代码级别的性能评估
2. 对于各个独立的中间件或者公共服务组件本身,也会开展性能基准测试和容量规划测试
3. 对于基于微服务的各个 API 接口,会开展性能基准测试和压力测试
4. 对于前端 Web 页面,会开展基于前端性能的调优
5. 对于整体系统,会不定期开展全链路压力测试,还可用历史流量回放等技术来模拟海量实际的并发场景