说明
因掘金一文章不能超过2W字,因此分开上下篇;
前言
本文阅读来自极客,课程链接如下: time.geekbang.org/column/103
作者:茹炳晟
个人感想
先把个人感想列出来吧,全文很长,耐心看,可能要20分钟+,部分内容会比较详,阅读体验可能会比较差,因为都是文字为主;
总的来说,这个课程值这个价格,看完了之后,觉得知识面广了很多,不一定对实际工作有用,但是用来扩展知识面,是一个很不错的课程,同时也了解其他企业是怎么做的,建议小伙伴都看一下;
通过这个课程目录,其实也可以看得出,很多大公司的测试工程师,并不带带是业务测试,具备开发能力是必要条件,因为也希望能给自己一个警惕;
如果非要推荐,第5章的自动化测试工具跟35章的数据构建值得一看,里面的内容很丰富;
这个课程,陆陆续续看了好久,看的很认真,包括评论都不放过,因此看到有意思的评论,都会贴出来;
下面的内容都是亲手打出来的,因为jb依然相信,自己写/打出来的东西,印象会加深~
作者的软件测试修炼之道
经历过的变革:
- 自动化测试用例设计与开发
- 测试框架选型
- 测试框架自行研发
- 测试框架架构设计
- 测试服务化
趋势总结:
- 自动化测试在软件质量工程中的地位发生了质的变化,从原来的已自动化测试为辅,但现在以自动化测试为主
- 产品迭代周期缩短,需要一套完善的高并发测试执行基础架构支持
- 合格的测试工程师关注的是纯粹的测试,优秀的测试工程师关心的是整体交付的质量
- 如何构建低维护成本,可以灵活组装的自动化脚本,涉及到分层设计模型
1)真的懂测试吗?从用户登录测试谈起
测试场景
输入用户名和密码,点击确认按钮,验证是否登录成功即可
最常用黑盒测试方法
等价类划分
将所有可能的输入数据划分成若干个子集,在每个子集中,如果任意一个输入数据对于揭露程序中潜在错误都具有同等效果,这样的子集就构成一个等价类;
边界值分析
是选取输入、输出的边界值进行测试; 简单描述就是正好等于、刚刚大于、刚刚小于;
边界值算是对等价类划分的补充
穷尽测试
指包含了软件输入值和前提条件所有可能组合的测试方法,完全穷尽测试的系统里应该不残留任何未知的软件缺陷;
但这种做法不实际,因为受时间成本,一般采用基于风险驱动的模式,有所侧重地选择测试范围和设计测试用例;
显式功能性测试用例
显式功能性需求指的是软件本身需要实现的功能;
基于等价类划分和边界值分析方法:
输入已注册的用户名和正确的密码,验证是否登录成功;
输入已注册的用户名和不正确的密码,验证是否登录失败,并且提示信息正确;
输入未注册的用户名和任意密码,验证是否登录失败,并且提示信息正确;
用户名和密码都为空,验证是否登录失败,并且提示信息正确;
用户名额密码两者之一为空,验证是否登录失败,并且提示信息正确;
如果登录功能启用了验证码功能,在用户名和密码正确的前提下,输入正确的验证码,验证是否登陆成功;
如果登录功能启用了验证码功能,在用户名和密码正确的前提下,输入错误的验证码,验证是否登陆失败,并且提示信息正确;
复制代码
其他场景用例:
用户名和密码是否大小写敏感;
页面上的密码框是否加密显示;
后台系统创建的用户第一次登陆成功时,是否提示修改密码;
忘记用户名和忘记密码的功能是否可用;
前端页面是否根据设计要求限制用户名和密码长度;
如果登陆功能需要验证码,点击验证码图片是否可以更换验证码,更换后的验证码是否可用;
刷新网页是否会刷新验证码;
如果验证码具有时效性,需要分别验证时效内和时效外验证码的有效性;
用户登录成功但是会话超时后,继续操作是否会重定向到用户登录界面;
不同别的用户,比如管理员用户和普通用户,登录系统后的权限是否正确;
页面默认焦点是否定位在用户名的输入框里;
快捷键Tab和Enter等,是否可以正常使用;
网络延迟或者弱网或者切换网络或者断网时正常登陆是否正常
是否支持第三方登陆;
是否可记住密码,记住的密码保存是否加密;
记住密码是否有有效期,过期之后是否会清空密码;
用户名密码是否支持特殊字符和中文;
是否可以使用登录的api发送登录请求,并绕开验证码校验;
是否可以用抓包工具抓倒的请求包直接登录;
截取到的token等信息,是否可以在其他终端上直接使用,绕开登录;
后端是否有校验内容的格式长度;
登录后输入登录URL,是否还能再次登录;
登录错误后的提示是否有安全隐患;
密码强弱型校验;
空和输入空字符串的校验是否一致;
安全性方面异地登录校验、更换设备登录校验;
设备互斥;
密码错误限制次数;
输入账号密码时对键盘格式是否有要求;
密码一栏是否需要设置明暗码切换按钮;
输入账号密码格式不规范时是否将按钮设置为不可点击;
输入栏是否设置快速删除按钮;
多设备多平台同账号登录是否有互踢机制;
修改密码后,前密码不生效;
用户名规则、密码规则;
前后台切换,横竖屏切换;
复制代码
非功能性需求
主要涉及安全性、性能和兼容性测试;
安全性测试用例:
用户密码后台存储是否加密;
用户密码在网络传输过程中是否加密;
密码是否具有有效期,密码有效期到期后,是否提示需要修改密码;
不登录的情况下,在浏览器中直接输入登录后的url地址,验证是否会重定向到用户登录页面;
密码输入框是否不支持复制和粘贴;
密码输入框内输入的密码是否都可以在页面源码模式下被查看;
用户名和密码的输入框中分别输入典型的SQL注入攻击字符串,验证系统的返回页面;
用户名和密码的输入框中分别输入典型“XSS跨站脚本攻击”字符串。验证系统行为是否被篡改;
连续多次登录失败情况下,系统是否会阻止后续的尝试以应对暴力破解;
同一用户在同一终端的多种浏览器上登录,验证登录功能的互斥性是否符合设计预期
同一用户先后在多台终端的浏览器上登录,验证登录是否具有互斥性;
用户登陆后存储在数据库中的用户个人信息是否加密;
用户登陆过程中log中是否有个人信息明文打印;
是否使用到缓存;
复制代码
性能压力测试用例:
单用户登录的响应时间是否小于3秒;
单用户登录时,后台请求数据是否过多;
高并发场景下用户登录的响应时间是否小于5秒;
高并发场景下服务端监控指标是否符合预期;
高集合点并发场景下,是否存在资源死锁和不合理的资源等待;
长时间大量用户连续登录和登出,服务器端是否存在内存泄露;
同时支持10个用户登录,同时9个或者11个用户登录是否正确或者提示信息正确;
复制代码
兼容性测试用例:
不同浏览器下,验证登录页面的显示以及功能正确性
相同浏览器的不同版本下,验证登录页面的显示以及功能正确性
不同移动设备终端的不同浏览器下,验证登录页面的显示以及功能正确性
不同分辨率的界面下,验证登录页面的显示以及功能正确性
复制代码
小结
1)用例设计需要考虑明确的显示功能性需求,还要考虑兼容性、安全性、性能等一系列的非功能性需求;
2)用例设计是不可穷尽的,受限于时间成本,因此需要兼顾缺陷风险和研发成果之间的平衡;
2)如何设计一个“好的”测试用例
能够覆盖所有等价类以及各种边界值的用例都是好用例;
好的测试用例必须具备的特征
整体完备性
是一个完备的整体,是有效测试用例组成的集合,能够完全覆盖测试需求;
等价类划分的准确性
对于每个等价类都能保证只要其中一个输入测试通过,其他输入也一定测试通过;
等价类和的完备性
保证所有可能的边界值和边界条件都已经正确识别;
3种最常用测试用例设计方法
等价类划分
等价类中任意一个输入数据对于揭露程序中潜在错误都具有同等效果;
成绩系统,取值范围0-100之间的整数,及格60分;
有效等价类:
0-59之间的任意整数;
59-100之间的任意整数;
无效等价类:
小于0的负数;
大于100的整数;
0-100之间的任何浮点数;
其他恩义非数字字符;
复制代码
边界值分析
成绩系统:-1、0、1、59、60、61、99、100、101
复制代码
错误推测方法
指基于对被测试软件系统设计的理解、过往经验以及个人直觉,推测出软件可能存在的缺陷,从而有针对性地设计测试用例的方法,类似于探索性测试;
复制代码
如何才能设计出好的测试用例
在具体的用例设计时,首先需要搞清楚每一个业务需求所对应的多个软件功能需求点,然后分析出每个软件功能需求点对应的多个测试需求点,再针对每个测试需求点设计测试用例;
2个关键点:
- 从软件功能需求出发,全面地、无遗漏地识别出测试需求
- 对于每个测试需求点,需要综合运用等价类划分、边界值分析和错误推测方法来全面设计用例
用例设计的其他经验
只有深入理解被测试软件的架构,才能设计出更好的测试用例,去发现系统边界值以及潜在缺陷;
必须深入理解被测软件的设计和实现细节,深入理解软件内部的处理逻辑;
需要引入需求覆盖率和代码覆盖率来衡量测试执行完备性;
小结
1)好的测试用例一定是一个完备的集合,能够覆盖所有等价类以及各种边界值; 大部分情况是采用事后的角度,从漏测率和问题严重程度来评估用例的好坏;
2)设计而是用例的方法有很多种,但综合运用等价类划分、边界值分析和错误推测方法,能满足绝大多数的需求; 需求合理性测试,即用户体验测试;
3)好的测试用例再设计时,需要从软件功能需求出发,全面、无遗漏的识别出测试需求;
4)想设计好一个好的测试用例,必须要深入理解被测软件的架构设计,深入软件内部的处理逻辑,并且使用需求覆盖率和代码覆盖率来衡量测试执行的完备性;
3)什么是单元测试?如何做好单元测试
什么是单元测试
单元测试是指,对软件中的最小可测试单元在与程序其他部分相隔离的清空下进行检查和验证的工作,这里的最小和测试单元通常是指函数或者类;
单元测试一般以自动化的方式执行,在大量回归测试的场景下更能带来高收益;
电视机的生成、测试和软件的开发、测试对比:
电子元器件就像是软件中的单元,通常是函数或者类,对单个元器件的测试就像是软件测试中的单元测试;
组装完成的功能电路板就像是软件中的模块,对电路板的测试就像是软件中的集成测试;
电视机全部组装完成就像是软件完成了预发布的版本,点司机全部组装完成后的开机测试就像是软件中的系统测试;
复制代码
如何做好单元测试
概念
单元测试的对象是代码;
单元测试的主要手段有驱动代码、桩代码、mock代码;
手段
代码的基本特征与产生错误的原因:
- 条件分支、循环处理和函数调用
- 如果有任何一个分类遗漏、分类错误、分类没有正确没有遗漏,但处理逻辑错误,都会产生缺陷
- 如果要实现正确的功能逻辑,会有哪几种正常的输入
- 是否有需要特殊处理的多种边界输入
- 各种潜在非法输入的可能性以及如何处理
单元测试用例详解
一般情况下,单元测试的用例是一个输入数据和预计输出的集合;
在明确了代码需要实现的逻辑功能基础上,什么输入,应该产生什么输出;
输入数据:
被测试函数的输入参数
被测试函数内部需要读取的全局静态常量
被测试函数内部需要读取的成员变量
函数内部调用子函数获得的数据
函数内部调用子函数改写的数据
嵌入式x系统中,在中断调用时改写的数据
复制代码
预计输出:
被测试函数的返回值
被测试函数的输出参数
被测试函数所改写的成员变量
被测试函数所改写的全局变量
被测试函数中进行的文件更新
被测试函数中进行的数据库更新
被测试函数中进行的消息队列更新
复制代码
驱动代码,桩代码和mock代码
驱动代码是用来调用被测函数的;
驱动模块通常包括调用被测函数前的数据准备、调用被测函数以及验证相关结果三个步骤;
桩代码和mock代码是用来代替被测函数调用的真实代码;
桩代码是用来代替真实代码的临时代码;
桩函数内部实现:
- 桩代码的应用首先起到隔离和补齐的作用,使被测代码能够独立编译、链接、并独立允许
- 桩函数要具有与原函数完全相同的原形,仅仅是内部实现不同,这样测试代码才能正确链接到桩函数
- 用于实现隔离和补齐的桩函数比较简单,只需保持原函数的声明,加一个空的实现,目的是通过编译链接
- 实现控制功能的桩函数是应用最广泛的,要根据测试用例的需要,输出合适的数据作为被测函数的内部输入
mock代码跟桩代码非常类似,都是用来代替真实代码的临时代码;
mock跟桩代码的本质区别-测试期待结果的验证:
对于mock代码来说,关注点是mock方法有没有被调用,以什么样的参数被调用,被调用的次数,以及多个mock函数的先后调用顺序;
复制代码
对于桩代码,关注点是利用stub来控制被测函数的执行路径,不会去关注stub是否被调用以及怎么样被调用;
实际项目中如何开展单元测试
并不是所有的代码都要进行单元测试,通常只有底层模块或者核心模块的测试中才会采用单元测试;
需要确定单元测试框架的选型:
- Java,junit和TestNG
- C/C++,CppTestParasort C/C++test
引入代码覆盖率:
- Java,jacoco
- JavaScript,Istanbul
执行单元测试、代码覆盖率统计和持续集成流水线; 确保每次代码提交都会自动触发单元测试;
开展单元测试可能会遇到的问题
精密耦合的代码难以隔离;
隔离后编译链接运行困难;
代码本身的k可测试性较差,通常代码的可测试性和代码规模成正比;
无法通过桩代码直接模拟系统底层函数的调用;
代码覆盖率越往后越难提高;
复制代码
小结
1)介绍了单元测试概念,讨论了用例的组成,以及实际项目中开发单元测试的方法;
2)开展过程需要注意3个问题:
- 代码要做到功能逻辑正确,必须做到分类正确并且完备无遗漏,同时每个分类的处理逻辑必须正确;
- 单元测试是对软件中的最小和测试单元在与软件其他部分相隔离的情况下进行的代码级别;
- 桩代码起到了隔离和补齐的作用,使被测代码能够独立编译、链接,并运行;
4)为什么要做自动化测试?什么样的项目适合做自动化测试
什么是自动化测试
把人对软件的测试行为转化为由机器执行测试行为的一种实践;
本质就是写一段代码,然后去测试另外一段代码; 实现自动化测试用例本身属于开发工作,还需要随着被测对象的改变而不断更新,维护用例成本;
当自动化用例维护成本高于其节省的测试成本时,自动化测试失去了价值;
为什么需要自动化测试
优势
- 自动化测试可以替代大量的手工机械重复性操作,测试工程师可以把更多的时间花在更全面的用例设计和新功能的测试上;
- 自动化测试可以大幅提升回归测试的效率,非常适合敏捷开发过程;
- 自动化测试可以更好的利用无人值守时间,去更频繁的执行测试,特备适合现在非工作时间执行测试,工作时间分析失败用例的工作模式;
- 自动化测试可以高效实现某些受共呢个测试无法完成或者代价巨大的测试类型,比如关键业务7X24小时持续运行的系统稳定性测试和高并发场景的压力测试等;
- 自动化测试还可以保证每次测试执行的操作以及验证的一致性和可重复性,避免认为的遗漏或疏忽;
劣势
- 自动化测试并不能取代手工测试,只能替代手工测试中执行频率高、机械化的重复步骤;
- 自动测试远比手动测试脆弱,无法应对被测系统的变化; 自动化测试并不是全能,只是按部就班的执行事先定义好的测试步骤并验证结果;
- 自动化测试用例的开发工作量远大于单纯的手工测试 只有当开发完成的测试用例的有效执行次数大于等于5次时,才能收回自动化测试的成本;
- 手动测试发现的缺陷数量通常比自动化测试的要多;
- 测试的效率很大程度上依赖自动化测试用例的设计以及实现质量;
- 实行自动化测试的初期,用例开发效率通常都很低;
- 业务测试专家和自动化测试专家通常是两批人,前者懂业务不懂技术,后者懂技术不懂业务,需要两者配合;
- 自动化测试开发人员必须具备一定的编程能力;
什么样的项目适合自动化测试
- 需求稳定,不会频繁变更;
- 研发和维护周期长,需要频繁执行回归用例;
- 需要在多种平台上重复运行相同测试的场景;
- 某些测试项目通过手工测试无法重现,或者成本高;
- 被测软件的开发较为规范,能够保证系统的可测试性;
- 测试人员已经具备一定的编程能力;
小结
1)自动化测试是,把人工对软件的测试转化为由机器执行测试行为的一种实践,可以把测试工程师从机械重复的测试工作中解脱出来,将更多的经历放在新功能的测试和更全面的测试用例设计上;
2)自动化测试是一把双刃剑,一定程度上解放测试工程师的劳动力,完成一些人工无法实现的测试,但并非万能;一旦维护成本高于节省的测试成本,自动化就不合适了;
5)你知道软件开发各阶段都由哪些自动化测试技术吗
单元测试自动化技术
单元测试本身就是自动化测试,因为它根据软件详细设计采用等价类划分和边界值分析方法来设计测试用例,在测试代码段实现后再以自动化的方式统一执行;
广义上讲,单元测试阶段的自动化内涵不仅仅指测试用例执行的自动化,还应该包含以下5个方面:
- 用例框架代码生成的自动化;
- 部分测试输入数据的自动化生成;
- 自动桩代码的生成;
- 被测代码的自动化静态分析;
- 测试覆盖率的自动统计与分析;
用例框架代码生成的自动化
有些框架代码是由自动化工具生成的,而并非手工完成的,这样,开发者就可以把更多的精力放在测试逻辑的覆盖和测试数据的选择上;
比如selenium的代码可以通过录制生成,TestNG框架代码由自动化工具生成;
部分测试输入数据的自动化生成
这部分是指,自动化工具能够根据不同变量类型自动生成测试输入数据;
自动桩代码的生成
自动桩代码的生成是指自动化工具可以对被测试代码进行扫面分析,自动为被测函数内部调用的其他函数生成可编程的桩代码,并提供基于测试用例的桩代码管理机制;
那什么是抽桩?在单元测试阶段,假如函数A内部调用的函数B是桩代码,那么在代码级集成测试阶段,希望函数A不再调用假的函数B,而是调用真是的函数B,这个用真实函数B代替原本桩代码函数B的操作,就成为抽桩;
被测代码的自动化静态分析
静态分析主要指代码的静态扫描,目的是识别出违反编码规则或编码风格的代码;
通常这部分工作是结合项目具体的编码规则和编码风格,由自动化工具通过内建规则和用户自定义规则自动化完成的;
常用的静态代码分析工具有Sonar和Coverity;
测试覆盖率的自动统计与分析
单元测试用例执行结束后,自动化工具可以自动统计各种测试覆盖率,包括代码行覆盖率、分支覆盖率、MD/DC覆盖率等;
这些自动统计的指标,可以帮助衡量单元测试用例集合的充分性和完备性,并可以为你增补测试用例以提高测试覆盖率的依据;
代码级集成测试的自动化技术
简单的说,代码级集成测试是指将已经开发完成的软件模块放在一起测试;
代码级集成测试与单元测试最大的区别是,代码级集成测试中被测函数内部调用的其他函数必须是真实的,不允许使用桩代码代替,而单元测试仲允许使用桩代码来模拟内部调用的其他函数;
Web Server测试的自动化技术
Web Server测试,主要是指SOAP API和REST API这两类API测试,最典型的是采用SoapUI和Postman等类似的工具;
但这类测试工具基本都是界面操作手动发起requests并验证response,所以难以和CI/CD集成,于是乎就出现了API自动化测试框架;
如果采用API自动化测试框架来开发测试用例,那么这些测试用例的表现形式就是代码,如下:
对于基于代码的API测试用例,通常包含3个步骤:
- 准备API调用时需要的测试数据;
- 准备API的调用参数并发起API的调用;
- 验证API调用的返回结果;
目前最流行的API自动测试框架是REST Assured,可以方便的发起Restful API调用并验证返回结果;
同样的,Web Server的自动化测试内涵不仅仅包括API测试用例执行的自动化,还包括以下几个方面:
- 测试脚手架代码的自动化生成;
- 部分测试输入数据的自动生成;
- response验证的自动化;
- 基于SoapUI或者Postman的自动化脚本生成;
测试脚手架代码的自动化生成
在开发API测试的过程中更加关心的是,如何设计测试用例的输入参数以及组合,以及在不同参数组合情况下response的验证;
这时候就需要测试脚手架代码的自动生成技术; 生成的测试脚手架代码包含被测试API的调用、测试数据与脚本的分离,以及response验证的空实现;
部分测试输入数据的自动生成
与单元测试的输入数据的自动化生成类似,唯一不同的是,单元测试针对的参数是函数输入参数和函数内部输入,而API测试对应的是API的参数以及API调用的Payload;
数据生成的原则同样遵循边界值原则;
response验证的自动化
对于API调用返回结果的验证,比较关注的点是返回状态码、Scheme结构以及具体的字段值;
核心思想是自动化比较两次相同API调用的返回结果,并自动识别出有差异的字段值,比较过程可以通过规则配置去掉诸如时间戳、会话ID(Session ID)等动态值;
基于SoapUI或者Postman的自动化脚本生成
SoapUI或者Postman都是单一调试,累积到一定程度,里面会有大量测试用例集,如果引入基于代码实现的API测试框架,这意味着这些大量的用例都需要用代码的方式重写一遍,这是非常恶心的;
建议是,开发一个自动化代码转换生成工具,这个工具的输入是SoapUI或者Postman的测试用例元数据(JSON文件),输出是符合API测试框架规范的基于代码实现的测试用例,好处是原来积累的用例可以直接转换成在CI/CD上可以直接接入的自动化测试用例;
新增的用例,可以在SoapUI或者Postman测试验证,没问题,直接转化成符合API测试框架规范的测试用例;
postman集成CICD,通过Postman+newman+jenkins,在Postman导出一个json文件,在另一个服务器部署newman,命令行执行Postman导出的json文件,然后直接在服务器用newman工具就能测试并生成测试报告;
robot framework+requestslibrary的方式来做API自动化测试,识别编程能力不强的团队;
GUI测试的自动化技术
核心思想是基于页面元素识别技术,对页面元素进行自动化操作,以模拟实际终端用户的行为并验证软件功能的正确性;
- 对于传统Web浏览器的GUI自动化测试,业内主流的开源方案采用Selenium,商业方案采用Micro Focus的UFT(前身是HP的QTP);
- 对于移动端原生应用,通常采用主流的Appium它对IOS环境集成了XCUITest,对Android环境集成了UIAutomator和Espresso;
小结
介绍了软件研发生缪功能周期各个阶段的自动化测试技术,包括单元测试、代码级集成测试、Web Service测试和GUI测试的自动化技术;
6)你真的懂测试覆盖率吗
测试覆盖率通常被用来衡量测试的充分性和完整性,从广义的角度来讲,测试覆盖率主要分为两大类: 面向项目的需求覆盖率和技术的代码覆盖率;
需求覆盖率
需求覆盖率是指测试对需求的覆盖程度,通常的做法是将每一条分解后的软件需求和对应的测试简历一对多的映射关系,最终目标是保证测试可以覆盖每个需求,以保证软件产品的质量;
通常会采用ALM,Doors和TestLink等需求管理工具来建立需求和测试的对应关系,以此计算测试覆盖率;
需求覆盖率统计方法属于传统瀑布模型下的软件工程实践,传统瀑布模型追求自上而下地制定计划、分析需求、设计软件、编写代码、测试和运维,属于重量级流程,已经很难适应当今胡丽娜网时代下的敏捷开发;
所以,一般的覆盖率,默认是指代码覆盖率,而并非需求覆盖率;
代码覆盖率
是指,至少被执行了一次的条目数占整个条目数的百分比;
如果条目数是语句,对应的就是代码行的覆盖率; 如果条目数是函数,对应的就是函数覆盖率; 如果条目数是路径,那么对应的就是路径覆盖率;
三种代码覆盖率指标
- 行覆盖率又称为语句覆盖率,指已经被执行到的语句占总可执行语句的百分比(不包括代码注释、空行或者头文件等);这是最常用也是最低要求的覆盖率指标;
- 判定覆盖又称分支覆盖,用以度量程序中每一个判定的分支是否都被测试到了,即代码中每个判断的取真分支和取嫁分支是否各被覆盖只是各一次;
- 条件覆盖是指,判定中的每个条件的可能取值至少满足一次,度量判定中的每个条件的结果TRUE 和FALSE是否都被测试到了;
代码覆盖率的价值
统计代码覆盖率的根本目的是找出潜在的遗漏测试用例,并有针对性的进行补充,同时还可以识别出代码中那些由于需求变更等原因造成的不可达的废弃代码;
代码覆盖率越高越能说明你的测试用例设计是充分且完备的,但测试的成本会随着覆盖率的提高以指数的方式迅速增加;
代码覆盖率的局限性
代码覆盖率反馈的仅仅是已有代码的哪些逻辑被执行过了,哪些逻辑还没有被执行过,但如果其他模块依赖这段代码,能确保一定没有问题吗?
其根本原因在于代码覆盖率的计算是基于现有代码;
高的代码覆盖率不一定能保证软件的质量,但低的代码覆盖率一定不能保证软件的质量;
代码覆盖率工具
JaCoCo是一款Java代码的主流开源覆盖率功能,很方便的嵌入到Ant、Maven,并且可以很好的集成到jenkins等主流持续集成工具;
JaCoCo的代码覆盖率报告长啥样的?
如图所示,包括了每个Java代码文件的行覆盖率以及分支覆盖率,并给出了每个Java代码文件的行数、方法数和类数等信息;
下图所示为每个Java文件内部详细的代码覆盖率情况,绿色行表示已经被覆盖,红色行表示尚未被覆盖,黄色行表示部分覆盖; 左侧绿色菱形块表示该分支已经被完全覆盖、黄色菱形块表示该分支仅被部分覆盖;
通过这个报告,就可以知道代码真实的执行情况,然后再可以针对性的设计测试用例;
前端可以考虑使用jest;
代码覆盖率工具的实现原理
实现代码覆盖率的统计,最基本的方法就是注入(Instrumentation); 注入就是在被测代码中自动插入用于覆盖率统计的探针(Probe)代码,并保证插入的探针代码不会给源代码带来任何影响;
对于Java代码来说,根据注入目标的不同,可以分为源代码(Source Code)注入和字节码(Byte Code)输入两大类; 基于JVM本身特性以及执行效率的原因,目前主流的工具基本都是使用字节码注入,注入的具体实现采用ASM技术;
ASM是一个Java字节码操纵框架,能被用来动态生成类或者增强既有类的功能,直接直接产生class文件,也可以在类被加载入JVM之前动态改变类行为;
根据注入发生的时间点,字节码注入又可以分为两大模式: On-The-Fly注入模式和Offline注入模式;
On-The-Fly注入模式
特点在于无需修改源代码,也无需提前进行字节码插桩,适用于支持Java Agent的运行环境;
优点是可以在系统不停机的情况下,实时收集代码覆盖率信息,缺点是运行环境必须允许使用Java Agent;
实现 On-The-Fly 模式,主要有两种技术方案:
- 开发自定义的类装载器(Class Loader)实现类装在策略,每次类加载前,需要在class文件中插入探针,早期的Emma就是使用这种方案实现的探针插入;
- 借助Java Agent,利用执行在main()方法之前的拦截器方法premain()来插入探针,实际使用过程中需要在JVM的启动参数中添加"-javaagent"并制定用于实时字节码注入的代理程序,这样代理程序在装在每个class文件前,先判断是否已经插入了探针,如果没有则需要将探针插入class文件中,目前主流的JaCoCo就是使用这种方式;
Offline注入模式
Offline 模式也无需修改源代码,但是需要在测试开始之前先对文件进行插桩,并事先生成插过桩的class文件;
这种方式适用于不支持Java Agent的运行环境,以及无法使用自定义类装载器的场景;
优点是JVM启动时不再需要使用Java Agent额外开启代码,缺点是无法实时获取代码覆盖率信息,只能在系统停机时下获取;
Offlinne模式根据是生成新的class文件还是直接修改原class文件,又可以分为Replace和Inject两种不同模式;
和On-The-Fly注入模式不同,Replace和Inject的实现是在厕所运行前就已经通过ASM将探针插入了class文件,而在测试的运行过程中不需要任何额外的处理;
Cobertura就是使用Offline模式的典型代表;
小结
测试覆盖率通常被用来衡量测试的充分性和完整性,包括面向项目的需求覆盖率和偏向技术的代码覆盖率;
而需求覆盖率的统计方式不再适用于现在的敏捷开发模式,所有现在谈到的测试覆盖率,大多是指代码覆盖率;
高的代码覆盖率不一定能保证软件的质量,因为代码覆盖率是基于现有代码,无法发现那些未考虑某些输入已经未处理某些情况形成的缺陷;
7)如何高效填写软件缺陷报告
缺陷报告是测试工程师与开发工程师交流沟通的重要桥梁,也是测试工程师日常工作的重要输出,把发现的缺陷准确无歧义地表达清楚,是测试工程师最基本的一项技能;
准确无歧义的表达就意味着,开发工程师可以根据缺陷报告快速理解缺陷,并精确定位问题;开发经理可以准确预估缺陷修复的优先级;产品经理可以额了解缺陷对用户或业务的影响以及严重性;
缺陷报告本身的质量将直接关系到缺陷被修复的速度以及开发工程师的效率,同时还会影响测试工程师的信用、测试与开发人员写作的有效性;
缺陷标题
缺陷标题通常是别人最先看到的部分,是对缺陷的概括性描述,通常使用"在什么情况下发生了什么问题"的模式;
描述不仅要做到清晰简洁,最关键是要足够具体,切忌不能采用过于笼统的描述;描述的同时还必须清楚地表述发生问题的上下文,也就是问题出现的场景;
而且缺陷的标题不易过长,对缺陷更详细的描述应该放在缺陷概述里;
缺陷概述
缺陷概述通常会提供更多概括性的缺陷本质与现象的描述,是缺陷标题的细化;
缺陷概述的目的,清晰简洁地描述缺陷,使开发工程师能够聚焦缺陷的本质;
缺陷影响
缺陷影响描述的是,缺陷引起的问题对用户或者对业务的影响范围以及严重程度;
缺陷影响决定了缺陷的优先级(Priority)和严重程度(Severity),开发经理会以此为依据来决定修复该缺陷的优先级;产品经理会以此为依据来衡量缺陷的严重 程度,并觉得是否要等该缺陷被修复后才能发布产品;
准确描述缺陷影响的前提是,必须对软件的应用场景以及需求有深入的理解,这也是对测试工程师业务基本功的考验;
环境配置
环境配置用以详细描述测试环境的配置细节,为缺陷的重现提供必要的环境信息,比如操作系统类型和版本、被测软件软包、浏览器的种类和版本、被测软件的配置信息、集群的配置参数等等;
需要注意的是,环境配置的内容是按需描述,就是只描述那些重现缺陷的环境敏感信息;
前置条件
前置条件是指测试步骤开始前系统应该处在的状态,目的是减少缺陷重现步骤的描述,排除不必要的干扰,使其更有针对性;
缺陷重现步骤
缺陷重现步骤是整个缺陷报告中最核心的内容,目的在于用简洁的语言像开发工程师展示缺陷重现的具体操作步骤;
需要注意的是,操作步骤通常是从用户角度出发来描述,每个步骤都应该是可操作且连贯的;
在写缺陷重现步骤时,要反复执行这些步骤确认:
- 确保缺陷的可重现性;
- 找到最短的重现路径;
而对于缺陷重现步骤的描述,应该要避免3个场景问题:
- 笼统的描述,缺乏可操作的具体步骤;
- 出现与缺陷重现不相关的步骤;
- 缺乏对测试数据的相关描述;
期望结果和实际结果
在描述重现步骤的过程中,需要明确说明期望结果和实际结果; 期望结果来自于对需求的理解,而实际结果来自于测试执行的结果;
优先级(Priority)和严重程度(Severity)
根据百度百科的解释,缺陷优先级是指缺陷必须被修复的紧急程度,而缺陷严重程度是指因缺陷引起的故障对软件产品的影响程度;
严重程度是缺陷本身的属性,通常确认后就不再变化,而优先级是缺陷的工程属性,会随着项目进度、解决缺陷的成本等因素而变动;
那缺陷优先级和严重程度是什么关系?
- 缺陷越严重,优先级越高;
- 缺陷影响的范围越大,优先级也会越高;
- 有些缺陷虽然从用户影响角度来说不算严重,但是会妨碍测试或者是自动化测试的执行,这类缺陷属于典型的严重程度低,但优先级高的问题;
- 有些缺陷虽然严重程度比较高,但是考虑到修复成本以及技术难度,也会出现优先级较低的情况;
变通方案(Wordaround)
变通方案是提供一种临时绕开当前缺陷而不影响产品功能的方式,通常由测试工程师或者开发工程师完成,或者一同决定;
变通方案的有无以及实施的难易程度,是决定缺陷优先级和严重程度的重要依据;
根原因分析如
如果在发现缺陷的同时,定位出问题的根本原因,清楚地描述缺陷产生的原因并反馈给开发工程师,那么开发工程师修复缺陷的效率就会大幅提升;
附件
附件通常是为缺陷的存在提供必要的证据支持,常见的附件有界面截图、测试用例日志、服务器端日志、GUI测试的执行食品等;
小结
一份高效的软件缺陷报告,应该包括缺陷标题、缺陷概述、缺陷影响、环境配置、前提条件、缺陷重现步骤、期望结果和实际结果、优先级和严重程度、变通方案、原因分析、附件这几大块;
其他想说说
如何对一个阶段的BUG进行统计、分析并报告?
这种属于测试缺陷统计,bug趋势分析,bug收敛情况,bug模块分布,bug发现阶段统计等等,属于面向管理层和质量流程保障团队,一般这种都是利用缺陷管理系统的自带功能来生成类似的报告;
除了上面所需要的内容,还需要以下内容:
- 缺陷模块,方便后期统计;
- 能准确指派BUG给具体负责人,最简单说,先确认是后端还是前端的问题;
- 出现的概率;
- 对比性炎症;
8)以终为始,如何才能做好测试计划
在早期的软件工程实践中,软件测试计划的制定通常是在需求分析以及测试需求分析完成后开始,并且是整个软件研发声明周期中的重要环节;
没有测试计划会怎么样?
如果没有测试计划,会带来什么问题?
- 很难确切地知道具体的测试范围,以及应该采取的具体测试策略;
- 很难预估具体的工作量和所需要的测试工程师数量,同时还会造成各个测试工程师的分工不明确,引发某些测试工作被重复执行,而有些测试则被遗漏的问题;
- 测试的整体进度完全不可控,甚至很难确切知道母亲测试的完成情况,对于测试完成时间就更难预估准确的时间节点;
- 整个项目对潜在风险的抵抗能力很弱,很难应对需求的变更以及其他突发事件;
从这些问题,可以逆向思维推导出,一份好的测试计划要包括以下几点: 测试范围、测试策略、测试资源、测试进度和测试风险预估;
测试范围
顾名思义,测试范围描述的是被测对象以及主要的测试内容;
测试范围的确定通常是在测试需求分析完成后进行,所以确定测试范围的过程在一定程度上也是对测试需求分析的进一步校验,有助于在早期阶段发现潜在的测试遗漏;
测试策略的话题
测试策略简单来讲就是需求明确"先测什么后测什么"和"如何来测"两个问题;
测试策略会要求明确测试的重点,以及各项测试的先后顺序;
测试策略还需要说明,采用什么样的测试类型和测试方法;不仅要给出为什么要选用这个测试类型,还要详细说明具体的实施方法;
功能测试
根据测试需求分析的思维导图来设计测试用例;
这里需要注意,要先实现主干业务流程的测试自动化;
对于需要手工测试的测试点,要决定采用什么类型的测试用例设计方法,以及如何准备相关的测试数据;
还要评估被测软件的可测试性,如果有可测试性的问题,需要提前考虑切实可行的变通方案,甚至要求开发人员提供可测试性的接口;
兼容性测试
Web测试需要确定覆盖的浏览器类型和版本,移动设备测试需要确定覆盖的设备类型和具体IOS/Android的版本等;
那怎么确定需要覆盖的移动设备类型以及IOS/Android的版本列表?
- 如果旧产品,通过产品统计数据分析历史数据,得出Top30%的移动设备机型及版本信息,兼容性覆盖这批固件即可;
- 如果是一个新产品,可以通过类似TalkingData这种网站来查看目前主流的移动设备,分辨率大小、IOS/Android等信息来确定测试范围;
一般来说,兼容性测试是功能测试的后期,等功能基本稳定后,才会开始兼容性测试;
假如是接入一些新框架,这时候就需要评估接入新框架的信息,此时就需要先进行兼容性测试,以确保后期不会引入无法解决的兼容性问题;
而兼容性用例的选取,基本上是来自于已经实现的自动化测试用例;
性能测试
需要在明确了性能需求(并发用户数、响应时间、事务吞吐量、CPU、内存、IO、带宽、事务成功率、超时错误率等)前提下,结合被测系统的特点,设计性能测试场景并确定性能测试框架;
比如,是直接在 API 级别发起压力测试,还是必须模拟终端用户行为进行基于协议的压力测试。再比如,是基于模块进行压力测试,还是发起全链路压测。
如果性能是背景数据敏感的场景,还需要确定背景数据量级与分布,并决定产生背景数据的技术方案,比如是通过 API 并发调用来产生测试数据,还是直接在数据库上做批量 insert 和 update 操作,或者是两种方式的结合。
不管采用哪种方式,都需要明确待开发的单用户脚本数量,以便后续能够顺利组装压测测试场景;
性能测试的实施,是一个比较复杂的问题; 需要根据你想要解决的问题,确定性能测试的类型,然后根据具体的性能测试类型开展测试;
- 性能测试的实施,要先根据业务场景来决定需要开发哪些单用户脚本,脚本的开发会涉及到很多性能测试脚本特有的概念,比如思考时间、集合点、动态关联等;
- 脚本开发完成后,要以脚本为单位组织测试场景(Scenario),场景定义简单来说就是百分之多少的用户在做登录、百分之多少的用户在做查询、每个用户的操作步骤之间需要等待多少时间、并发用户的增速是X秒一个等;
- 最后,才是具体的测试场景执行,和自动化功能测试不同,性能测试执行完成后的性能测试报告的解读,是整个测试过程中最关键的点;
测试资源
测试资源通常包括测试人员和测试环境,这两类的资源是有限的;
而测试计划的目的就是,保证在有效资源下的产出最大化,所以,测试资源就是需要明确"谁来测"和"在哪里测"和"怎么测"的问题;
测试人员是最重要的,直接关系到整个测试项目的成败和效率,测试人员的资源通常由2个维度:
- 测试工程师的数量;
- 测试工程师的个人经验和能力;
测试进度
测试进度主要描述各类测试的开始时间,所需工作量,预计完成时间,并以此为依据来建议最终产品的上线发布时间;
在传统瀑布模型中,测试进度完全依赖于开发完成并递交测试版本的时间;如果开发案提交测试版本发生了延误,那么在不裁剪测试需求的情况下,产品整体的上线时间就同样会延期;
然而在敏捷模式下,测试会贯穿于整个开发过程,很多测试工作会和开发工作同步进行,这样测试进度就不会完全依赖于开发递交可以测试版本的时间;
行为驱动开发(Behavior-Driven-Development),就是平时所说的BDD,只的是可以通过自然语言书写非程序员可读的测试用例,并通过StepDef来关联基于自然语言的步骤描述和具体的业务操作,最典型的框架就是Cucumber;
测试风险评估
计划赶不上变化,通常需求表更、开发延期、发现重大缺陷和人员变动是引入项目测试风险的主要原因;
所以,在制定测试计划的时候,就要预估整个测试过程中可能存在的潜在风险,以及当这些风险发生时的应对策略;
小结
一份成功的测试计划,必须清楚的描述:测试范围、测试策略、测试资源、测试进度和测试风险预估这5个重要的方面;
- 测试访问需要明确测什么和不测什么;
- 测试策略需要明确先测什么后测什么和如何来测;
- 测试资源需要明确谁来测和在哪里测;
- 测试进度是需要明确各类测试的开始时间,所需工作量和估计完成时间;
- 测试风险预估是需要明确如何有效应对各种潜在的变化;
其他想说
在快速迭代过程,建议增加产品需求测试,意义在开发和测试开发前,保证所有人对需求理解的一致性;
9)软件测试工程师的核心竞争力是什么
目前测试工程师分为两大类别,一类是做业务功能测试,另一类是做测试开发的;
传统测试工程师应该具备的核心竞争力
按照对测试工程师重要程度进行排序,如下:
- 测试策略设计能力
- 测试用例设计能力
- 快速学习能力
- 探索性测试思维
- 缺陷分析能力
- 自动化测试技术
- 良好沟通能力
测试策略设计能力
这项是核心竞争力;
测试策略设计能力是指,对于各种不同的被测软件,能够快速准确地理解需求,并在有限的时间和资源下,明确测试重点以及最适合的测试方法的能力;
具备出色的测试策略设计能力,可以非常明确地回答出测试过程中遇到的这些关键问题:
- 测试要具体执行到什么程度;
- 测试需要借助什么工具;
- 如何运用自动化测试以及自动化测试框架,以及如何选型;
- 测试人员自愿如何合理分配;
- 测试进度如何安排;
- 测试风险如何应对;
出色的测试策略设计能力,不是一朝一夕的事情,需要经历大量的实际历练,还要保持持续思考,主动去提炼共性的内容;
测试用例设计能力
测试用例设计能力是指,无论对于什么类型的测试,都能设计出高效地发现缺陷,保证产品质量的优秀测试用例;
要做好测试用例设计,不仅需要深入理解被测软件的业务需求和目标用户的使用习惯,还要熟悉软件的具体设计和运行环境,包括技术架构、缓存机制、中间件技术、第三方服务集成等等。
要想提高测试用例设计能力,平时就要多积累,对常见的缺陷模式、典型的错误类型以及遇到过的缺陷,要不断地总结、归纳,才能逐渐形成体系化的用例设计思维。
还可以阅读一些好的测试用例设计实例开阔思路;
快速学习能力
包含两个层面的含义:
- 对不同业务需求和功能的快速学习与理解能力;
- 对于测试新技术和新方法的学习与应用能力;
学习工具,直接看官方文档会更快,因为里面的内容肯定是最新最权威的;
在学习新内容时,一定要做到理解其原理,而不是只停留在表面的、简单的操作和使用,长期保持这种学习状态,可以在很大程度上提高逻辑思维和理解能力;
探索性测试思维
探索性测试是指测试工程师在执行测试的过程中不断学习被测系统,同时结合基于自己经验的错误猜测和逻辑推理,整理和分析出更多的针对性的测试关注点;
本质上,探索性测试思维是测试用例设计能力和快速学习能力结合的必然结果;
优秀的探索性测试思维可以帮助实现低成本的精准测试,精准测试最通俗的理解可以概括为针对开发代码的变更,目标明确并且有针对性地对变更点以及变更关联点做测试,也是目前敏捷测试主推的测试时之一;
缺陷分析能力
包含3个层面的含义:
- 对于已经发现的缺陷,结合发生错误的上下文以及后台日志,可以预测或者定位缺陷的发生原因,甚至可以明确指出具体出错的代码行,由此可以大幅缩短缺陷的修复周期,并提高开发工程师对于测试工程师的认可以及信任度;
- 根据已经发现的缺陷,结合探索性测试思维,推断同类缺陷存在的可能性,并由此找出所有相关的潜在缺陷;
- 可以对一段时间内所发生的缺陷类型和缺陷进行合理分析,由点到面预估整体质量的健康状态,并能够对高频缺陷类型提供系统性的发现和预防措施,并以此类调整后续的测试策略;
这3个层面是依次递进的关系,越往后越能提现出测试工程师的核心竞争力;
自动化测试技术
自动化测试的核心价值还是测试本身,自动化仅仅是手段;
良好的沟通能力
测试工程师在项目中的作用,有点想润滑剂:
- 一方面,需要对接产品经理和项目经理,已确保需求的正常实现和项目整体质量的达标;
- 另一方面,还要和开发人员不断的沟通、协调,确保缺陷的及时修复和验证;
沟通能力会直接影响事务开展的效率,是一块敲门砖;
测试开发工程师的核心竞争力
测试系统需求分析能力
除了代码开发能力,测试开发工程师要具备测试系统需求分析能力; 要能够站在测试架构师的高度,识别出测试基础架构的需求和提高效率的应用场景;
更宽广的知识体系
测试开发工程师需要具备非常宽广的知识体系,你不仅需要和传统的测试开发工程师打交道,因为他们是你构建的测试工具或者平台的用户; 而且还要和 CI/CD、和运维工程师们有紧密的联系,因为你构建的测试工具或者平台,需要接入到 CI/CD 的流水线以及运维的监控系统中去。
除此之外,你还要了解更高级别的测试架构部署和生产架构部署、你还必须对开发采用的各种技术非常熟悉。。
性能测试工程师
性能测试工程师的核心价值不在于会多少性能测试工具,而是对于性能问题的直觉和定位能力,这个需要靠扎实的基础理论知识和大量的实践才能培养出来;
小结
测试工程师按照工作内容,分为了功能测试工程师和测试开发工程师两类;
对于功能测试工程师来说,其核心竞争力包括:测试策略设计能力、测试用例设计能力、快速学习能力、探索性测试思维、缺陷分析能力、自动化测试技术和良好的沟通能力这七大部分,你可以有针对性地提升自己某方面的能力,去获取更大发展空间的“敲门砖”。
而对于测试开发工程师来说,你需要具备优秀的测试系统需求分析能力和完备的知识体系,这样才能保证你设计的测试工作和平台,可以更好地满足提升测试效率的要求。
为什么测试会迷茫
从来没有见过开发会迷茫,往往是测试在迷茫,想了很久,也许是这些原因吧:
- 开发学习线路提和发展路线清晰,测试大多数是摸着石头过河;尤其对于功能测试入门,就更容易迷茫;
- 测试工程师涉及的内容太广泛,什么都要懂,但是什么都不精,接着就不知道职业该如何发展,每天被各种杂事淹没;
- 性能测试、自动化测试瓶颈大,自学开发难度大;
10)软件测试工程师需要掌握的非测试知识有哪些
开发工程师通常是深度遍历,关注的是点; 而测试工程师通常是广度遍历,关注的是面;
- 小到 Linux/Unix/Windows 操作系统的基础知识,Oracle/MySQL 等传统关系型数据库技术,NoSQL 非关系型数据库技术,中间件技术,Shell/Python 脚本开发,版本管理工具与策略,CI/CD 流水线设计,F5 负载均衡技术,Fiddler/Wireshark/Tcpdump 等抓包工具,浏览器 Developer Tool 等;
- 大到网站架构设计,容器技术,微服务架构,服务网格(Service Mesh),DevOps,云计算,大数据,人工智能和区块链技术等。
基本上看,所有都覆盖到了。。但人的时间是有限的,肯定不会这样逐个介绍,因此调了点占作比较重要的;
网站架构的核心知识
除了功能测试,还要性能测试、稳定性测试、全链路压测、故障切换测试、动态集群容量伸缩测试、服务降级测试和安全渗透测试;
- 比如,如果不清楚 Memcached 这类分布式缓存集群的应用场景和基本原理,如果不清楚缓存击穿、缓存雪崩、缓存预热、缓存集群扩容局限性等问题,就设计不出针对缓存系统特有问题的测试用例;
- 再比如,如果对网站的可伸缩性架构设计不了解,不清楚应用服务器的各种负载均衡实现的基本原理,不了解数据库的读写分离技术,就无法完成诸如故障切换、动态集群容量伸缩、服务降级等相关的测试,同时对于性能测试和全链路压测过程中可能遇到的各种瓶颈,也会很难定位和调整。
容器技术
对测试开发工程师来说,需要应用容器的场景就更多了。比如,目前主流的 Selenium Grid 就已经提供了官方 Docker 版本,可以直接以容器的方式建立测试执行环境,也可以很方便地在 Pivotal Cloud Foundry 和 Google Cloud Platform 等云计算平台上快速建立测试执行环境。
基于 Docker 的 Selenium Grid 大大减轻了批量虚拟机节点上 Web Driver、浏览器版本和守护者进程版本等升级维护的工作量。
测试开发工程师还可以通过 Docker Image 的形式,提供某些测试工具,而不是以传统的安装包或者 JAR 文件的形式,可以实现测试工具开箱即用。
云计算技术
对于云计算的学习,侧重点应该是如何使用云提供的基础设施以及服务;
可以尝试用云服务去部署自己的应用,同时还可以结合云平台提供的各类服务(配置服务,数据库服务)和你的应用做集成;
DevOps思维
DevOps强调的是,开发、测试和运维等组织团队之间,通过高效自动化工具的写作和沟通,来完成软件的全声明周期管理,从而实现更频繁持续交付高质量的软件,根本目的是要提升业务的交付能力;
DevOps的具体表现形式可以是工具、方法和流水线,但起更深层次的内涵还是在思想方法,以敏捷和精益为核心,通过发现问题,以系统性的方法或者工具来解决问题,从而实现持续改进;
对于 DevOps,我建议的学习路径是,你可以从深入掌握 Jenkins 之类的工具开始,到熟练应用和组合各种 plugin 来完成灵活高效的流水线搭建,之后再将更多的工具逐渐集成到流水线中以完成更多的任务。
前端开发技术
从测试工程师的角度来讲,如果你能够掌握前端开发技术,也就意味着你可以更高效地做前端的测试,更容易发现潜在缺陷。同时,你还可以自己构建测试页面,来完成各类前端组件的精细化测试,大大提高测试覆盖率和效率。
关于前端技术的学习路径,通常你首先需要掌握最基本的 JavaScript、CSS、JQuery 和 HTML5 等知识,然后再去学习一些主流的前端开发框架,比如 Angular.js、Backbone.js 等。 当然现在的 Node.js 的生态圈非常发达,你如果能够掌握 Node.js,那么很多东西实现起来都可以得心应手。
总结
软件测试工程师需要掌握非常多的非测试专业知识,包括:网站架构、容器技术、云计算技术、DevOps 思维,以及前端开发技术的核心知识以及实践。
11)互联网产品的测试策略应该如何设计
研发流程的不同决定了测试策略的不同
传统产品跟互联网产品的研发本身最大的不同就是“快”;
发布周期的巨大差异决定了,传统软件产品的测试策略必然不适用于互联网产品的测试,两者的测试策略必然在测试执行时间和测试执行环境上有巨大差异;
发布流程通常包含了静态代码扫描、单元测试、编译、打包、上传、下载、部署和测试的全流程;
那如何在保证测试质量和测试覆盖率的前提下,有效缩短测试执行时间?
- 引入测试的并发执行机制,用包含大量测试执行节点的测试执行集群来并发执行测试用例; 测试执行集群,可以简单理解为是一批专门用来并发执行测试用例的机器;
- 其次,必须从测试策略上找到突破口;
传统软件产品的测试策略设计
传统软件产品的测试策略,就如下图的金字塔模型,在很长一段时间内,该金字塔模型都被认为是测试策略设计的最佳实践;
单元测试
金字塔最底部是单元测试,属于白盒测试的范畴,一般由开发工程师自己完成,由于越早发现缺陷其修复成本越低,所有传统软件产品的测试策略提倡对单元测试的高投入;
API测试
金字塔中部是API测试,主要针对的是各模块的暴露接口,通常采用灰盒测试方法;
灰盒测试方法是介于白盒测试和黑盒测试之间的一种测试技术,其核心思想是利用测试执行的代码覆盖率来知道测试用例的设计;
以API接口测试为例,首先以黑盒方式设计如何调用API的测试用例,同时在测试执行过程中统计代码覆盖率,然后根据代码覆盖率情况来补充更多、更有针对性的测试用例;
GUI测试
金字塔最上层的是GUI测试,也成为端对端(E2E,end-to-end)测试,是最接近软件真是用户使用行为的测试类型;
通常是模拟真是用户使用软件的行为,即模拟用户在软件界面上的各种操作,并验证这些操作对应的结果是否正确;
GUI测试的优点是,能够实际模拟真实用户的行为,直接验证软件的商业价值; 缺点是执行的代码比较大,既然使用自动化,用例的维护和执行代码依然很大;
另外,GUI测试的稳定性问题,是长期以来阻碍GUI测试发展的重要原因; 即时采用了很多诸如retry机制以及异常场景恢复机制等方式,GUI测试的随机失败率高居不下;
互联网产品的测试策略设计
对于互联网产品来说,金字塔模型已经不适用了;
GUI测试
互联网产品的上线周期,决定GUI测试不能大范围开展;
- 互联网产品的迭代周期,决定了留给开发GUI自动化测试用例的时间非常有限;
- 互联网产品客户端界面频繁变化,GUI自动化测试的效率非常低;
因此,互联网产品的GUI测试通常采用手工为主,自动化为辅的测试策略;
手工测试往往利用探索性测试思想,针对新开发或者新修改的界面功能进行测试,而自动化测试的关注点主要放在相对稳定且核心业务的基本功能验证上;
所以,GUI的自动化测试往往只覆盖最核心且直接影响主营业务流程的场景;
从用例数量来看,传统软件的GUI测试是重量级的,因为测试周期长;而互联网的GUI测试是轻量级的,毕竟上线周期太短了;
API测试
对于互联网产品,把测试重点放在API测试上,才是最明智的选择;
- API 测试用例的开发与调试效率比 GUI 测试要高得多,而且测试用例的代码实现比较规范,通常就是准备测试数据,发起 request,验证 response 这几个标准步骤;
- API 测试用例的执行稳定性远远高于 GUI 测试;
- 单个 API 测试用例的执行时间往往要比 GUI 测试短很多;
- 现在很多互联网产品采用了微服务架构,而对微服务的测试,本质上就是对不同的 Web Service 的测试,也就是 API 测试。
- API 接口的改动一般比较少,即使有改动,绝大多数情况下也需要保证后向兼容性(Backward Compatibility)。所谓后向兼容性,最基本的要求就是保证原本的 API 调用方式维持不变。
互联网产品的这些特性决定了,API 测试可以实现良好的投入产出比,因此应该成为互联网产品的测试重点。这也就是为什么互联网产品的测试策略更像是个菱形结构的原因。
下图就是菱形的测试策略,遵循“重量级 API 测试,轻量级 GUI 测试,轻量级单元测试”的原则;
单元测试
互联网产品通常会分为应用层和后端服务,后端服务又可以进一步细分为应用服务和基础服务;
后端基础服务和一些公共应用服务相对稳定,而且对于系统全局来说是“牵一发而动全身”,所以后端服务很有必要开展全面的单元测试; 而对于变动非常频繁的客户端应用和非公用的后端应用服务,一般很少会去做单元测试。
另外,对于一些核心算法和关键应用,比如银行网关接口,第三方支付集成接口等,也要做比较全面的单元测试。
总结来讲,互联网产品的全面单元测试只会应用在那些相对稳定和最核心的模块和服务上,而应用层或者上层业务服务很少会大规模开展单元测试。
小结
传统软件通常采用金字塔模型的测试策略,而如何的互联网产品往往采用菱形模型;
菱形模型有以下4个关键点:
- 以中间层的API测试为重点做全面的测试;
- 轻量级的GUI测试,只覆盖最核心直接影响主营业务流程的E2E场景;
- 最上层的GUI测试通常利用探索式测试思维,以人工测试的方式发现尽可能多的潜在问题;
- 裁员测试采用分而治之的思想,支队那些稳定并且核心的服务和模块开展全面的单元测试,而应用层或者上层业务只会做少量的单元测试;
12)从0到1:你的第一个GUI自动化测试
本章主要介绍自动化的思想及selenium的原理,这里不多说,感兴趣的同学可点击这里阅读;
13) 效率为王:脚本与数据的解耦 + Page Object模型
问题:测试脚本中既有测试数据又有测试操作,所有操作都集中在一个脚本
测试脚本和数据解耦
测试输入数据单独保存,测试脚本的输入数据采用变量代替;
常见的就是csv,每行读取;
复制代码
这种模式叫数据驱动;
好处在于:
- 解决了大量重复脚本的问题;
- 其他数据也可以存放到文件里做判断;
页面对象(Page Object)模型
页面对象模型”的核心理念是,以页面为单位来封装页面上的控件以及控件的部分操作。
而测试用例使用页面对象来完成具体的界面操作。
复制代码
14)更接近业务的抽象:让自动化测试脚本更好地描述业务
如何把控操作函数的粒度
操作函数的粒度是指,一个操作函数到底应该包含多少操作步骤才是最合适的;
复制代码
- 粒度过大,会降低函数的可重用性;
- 粒度过小,就失去函数封装的意义;
一般会以完成一个业务流程为主线,抽离出高内聚低耦合
的操作步骤集合;
业务流程抽象
业务流程抽象是,基于操作函数的更接近于实际业务的更高层次的抽象方式。
基于业务流程抽象实现的测试用例往往具有较好的灵活性,可以根据实际测试需求方便地组装出各种测试用例;
复制代码
业务流程的核心思想是,从业务的维度来指导测试业务流程的封装;
15)过不了的坎:聊聊GUI自动化过程中的测试数据
从创建的技术手段上来讲,创建测试数据的方法主要分为三种:
- API 调用;
- 数据库操作;
- 综合运用 API 调用和数据库操作;
从创建的时机来讲,创建测试数据的方法主要分为两种:
- 测试用例执行过程中,实时创建测试数据;
- 测试用例执行前,事先创建好“开箱即用”的测试数据;
16)GUI测试还能这么玩(Page Code Gen + Data Gen + Headless)?
-
GUI 测试数据自动生成,主要是基于测试输入数据的类型以及对应的自定义规则库实现的,并且对于多个测试输入数据,可以基于笛卡尔积来自动组合出完整的测试用例集合;
-
无头浏览器,就是在执行过程中看不到运行的界面,优点在于速度快,简化环境搭建;
-
页面对象自动生成技术,基本思路是,不用再手工维护
Page Class
了,只需要提供Web
的URL
,就会自动生成这个页面上所有控件的定位信息,并自动生成Page Class
,目前暂无开源,商业工具较多;