1、软件测试的基础概念(1)
文章目录
- 一、软件测试
- 1、软件测试(Software Testing)
- 2、缺陷(Defeat)
- 3、测试用例(Test Case)
- 4、测试金字塔
- 5、测试策略
- 6、测试左移和测试右移
- 7、质量度量
- 二、软件的测试分类
- 1、单元测试
- 2、集成测试
- 3、系统测试
- 4、验收测试
- 5、静态分析
- 6、安全测试
- 7、性能测试
软件测试是一个非常专业的工作,在大多数的软件建设组织中,都会有软件测试类的岗位(或类似名称的岗位)来对交付的软件进行验证。软件质量不仅仅是测试人员的工作,并且其也只能尽可能地找出存在的bug,并不能真正解决bug。bug产生于需求分析、系统设计以及编码,甚至测试本身,所以软件质量的提升或保证应该由软件产品参与的各个环节共同努力的结果。
一般来说,越早重视软件质量取得的价值就越大。事情一次性做对总比增加应对措施、预防措施等一系列的挽救措施要更节省成本,也更加的高效。
在输出以上观点后,我们接下来介绍几个基本的概念。
一、软件测试
1、软件测试(Software Testing)
测试是一种检验产品质量的活动。通常意义上的 “软件测试” 是特定环境下检查软件是否存在错误,以及能否满足业务需求和设计的活动或过程。软件的含义不仅仅是程序本身,还包括文档、数据和其他基础设施等一切交付物。这也是越来越多的公司将测试工程师(Software Test Engineer)的岗位转变为质量工程师 (Quality Assurance)的原因。
软件开发过程中,无论采用的是瀑布开发模式还是敏捷开发模式,甚至起他的开发模式,都会存在需求分析、系统设计、编码、测试、投入生产上线等过程。在软件全生命周期质量保证的理念下,基于软件过程的测试类别被更为细致的提出来,如需求测试、架构测试、设计测试、单元测试、集成测试、用户验证测试等。
想要明确定义软件的质量,对软件质量的度量非常重要。软件质量的度量指标和方法非常多样,比如有测试用例覆盖率、每千行代码的缺陷率等指标。在定义了软件质量的度量指标后,可根据质量保证计划中定义的度量指标值和执行结果进行对比分析,以便得出软件质量变化情况,然后给出相应的应对措施和预防措施。
现代的软件测试提倡软件全生命周期测试,项目开始时测试人员就需要参与对需求的验证和评审,即测试左移,而对软件上线后运营期质量提出要求,则是测试右移。
2、缺陷(Defeat)
如果软件产品没有按照我们的期望运行,我们会说软件有 Bug。
Bug 的原意是 “臭虫”,这个名称的来源是继电器计算机中飞进的一只飞蛾。被公认为世界上最早一批程序员中的葛丽丝·霍普女士在 Mark II 计算机上工作时,设备无法正常工作了,整个团队都不知道怎么回事。后来经过排查发现是一只飞蛾飞入设备内部引起的故障(Mark II 是一台继电器计算机,异物的侵入会导致元件无法工作)。葛丽丝·霍普女士在她的笔记中记录了这个故事,说明问题的根因是一个虫子引起的, Bug 这个词也流传了下来。
在软件工程领域,更多使用缺陷来描述软件没有按照预期运行的现象。缺陷描述的不仅仅是程序编码上的错误,还包括需求和系统设计的不合理,运营期间的配置问题,以及基础设施故障等。
缺陷的引入可能会发生在软件生命周期的任何一个环节中,使用正交缺陷分类(Orthogonal Defect Classification)法划分缺陷,具体如下。
- 需求缺陷:需求本身不合理或者缺乏系统性考虑,造成的bug。
- 设计缺陷:系统设计时未考虑到一些场景,或者在系统设计上不能满足一些特定场景,造成软件在使用过程中出现bug。
- 编码缺陷:由于开发者的疏忽或者其他原因,造成的bug。
- 配置缺陷:在投入生产使用的过程中,由于配置不合理或者环境发生变化造成的bug。
为了更清晰地描述缺陷这个概念,人们区分了以下几个概念。
- 缺陷(Defect):它是静态的,一直存在,也就是我们说的Bug。
- 错误(Error):程序执行有缺陷的代码或者输入特定的数据后,造成程序状态异常。
- 失效(Failure):失效是软件不能正常运行,使用者感知到的状态。一个软件可能有缺陷但是不一定会带来错误并被感知到,失效也有可能是非缺陷造成的,比如运行环境不满足要求等。
缺陷并不一定会导致程序运行错误,由缺陷导致程序发生错误,叫做缺陷的激活。缺陷往往需要在特定的条件和场景下才会被激活,例如,一些特别的输入或者运行环境发生变化。
根据优先级和严重性对导致问题的缺陷进行分类,比如将缺陷分为 4 个级别:
- P0 致命:非常严重的线上事故(比如让整个系统瘫痪),需要停下手上的工作立即修复。如果不能在一定时间内修复,需要上报,通过其他途径来解决(比如使用备用方案)。
- P1 严重:部分重要功能不可使用,虽然优先级没有 P0 那么高,但是也需要立即修复。
- P2 一般:次要功能不可使用,会给用户带来不便,可不立即修复,随版本进行常规发布。
- P3 轻微:会给用户带来不便,或者 UI、文案(视情况而定)上存在需要调整的内容,随版本进行常规发布。
说明:上述分类中的字母 P 是 Priority 的首字母缩写。
3、测试用例(Test Case)
测试用例(Test Case,TC)是一组测试输入和预期的集合。简单来说就是包含测试内容需要的输入、预期和结果,以及特定的测试环境。
在不同的软件开发模型中,测试用例呈现的方式不同。在瀑布模型中,通常会根据不同的版本来管理测试用例,并持续维护;使用敏捷的方式,测试用例往往跟随着用户故事管理测试用例;RUP(统一软件开发过程)则要求测试用例可以追溯验证系统行为,采用类似瀑布的方式维护测试用例,但是每个迭代都需要更新,并持续维护。
测试用例的规格在 IEEE 标准和国标(GB/T)上都有被定义过,主要包含如下内容:
- 被测试的对象,对应软件特性或者需求。
- 给予的条件,包括输入信息和测试环境,输入信息包含了测试数据和操作步骤(执行路径)。
- 期望的结果,包含软件的执行预期,即期待的程序输出。
如果能严格和良好地基于测试用例进行实践,可以用较小的成本覆盖大量的测试场景,并能准确地让问题重现。
在一些团队会使用思维导图作为测试用例,但是这种形式比较难以维护。越来越多的公司会创建自己的测试管理平台,思维导图则作为测试用例的补充。
编写好的测试用例,需要遵守如下的原则:
- 期望的结果可判定。测试用例有明确的判定标准,比如系统登录成功显示 “登录成功” 文案以及个人信息。
- 测试用例可重复执行。测试用例应该能被反复执行,并且结果保持稳定。
- 测试用例具有代表性。测试用例的设计应该从典型到特殊延展,并能覆盖核心业务场景。
让测试用例具有代表性是设计测试用例的难点,设计者需要从不同的角度选取测试场景,达到最优的测试性价比。有些公司会把正常的流程和符合预期的结果叫做正向用例,把一些异常处理的场景叫做反向用例。
另外最为关键的地方是设计测试数据时,需要考虑到大量的边界值。边界值指的是介于正常数据和错误数据之间的临界数据,比如 0 是正数和整数之间的一个边界值。用户输入往往难以穷尽,借助边界值作为代表性测试数据是一种常用的方式。
4、测试金字塔
软件测试有很多类型,测试金字塔的核心理念是:不同测试类型的收益和性价比是不一样的。
通常来说,基于界面的测试,自动化难度高,且为了能覆盖更多的的场景,并让测试正常运行,需要准备的数据量也更多,相应地投入的时间也会较多。
单元测试则不太一样,测试的目标更加精确,需要准备的测试数据量较少,同时单元测试运行得更快,因此投入的时间较少。
在 《Succeeding with Agile》书中提出了一个测试金字塔,形象地描述了UI测试、服务测试和单元测试的差异。下图是简单的测试金字塔,可以用来描述不同测试类型的执行速度和消耗资源的情况。
实际上测试金字塔中层次的划分取决于所采用的技术栈,并不拘泥于图中所示这三层。在微服务系统中,我们通常使用的测试金字塔可以描述为:单元测试、API 测试、界面测试。测试金字塔的每一层都可以选用不同的工具来实现自动化。
测试金字塔只是一种对测试划分方法的模型,这种模型可以有非常多的解释和变种。在一些测试金字塔中我们可能会看到手工测试、验收测试等内容,也可能会有非常多层。测试金字塔主要应用于敏捷过程的测试工作中,在其他的软件开发过程中也不同的测试模型,例如 V 模型。
5、测试策略
测试策略描述的是一个项目或者产品如何组织测试活动,以获取最大的价值。完整的测试策略就是一个项目的完整测试框架,涵盖了关于质量的各方面测试清单,以及对应的实施方式。测试策略可以是一份详尽的文档,也可以是一个图示或者一份简单的检查清单。
下图描述了敏捷团队活动中的测试实践。图中左下角使用了一个测试象限,描述哪些测试应该自动进行,中间展示的是一个四层的测试金字塔。测试金字塔中的测试实践为:单元测试、API 集成测试、端到端测试、探索式测试。
注:图片来源于 https://www.bylinzi.com/2020/01/10/one-page-test-strategy。
测试策略中需要包含如下内容。
- 测试原则:所有的实践都应该围绕这个原则展开,比如 “项目组为质量负责”。
- 测试范围:功能特性、性能、安全、可用性、可靠性等。
- 测试方法:需求和设计评审、静态代码分析、单元测试、集成测试、E2E 测试、安全建模、渗透测试、探索性测试等。
如果将测试策略延展,还可以包括各项软件质量度量的内容等。
6、测试左移和测试右移
测试左移是指在软件进入测试阶段之前就介入测试,QA(测试人员)在需求阶段就参与,并对设计阶段的各项活动进行评估。在需求澄清的时候就应该参与,并对需求、用户故事等输出进行检查。另外,在研发人员进入设计阶段后,也可以对输出的技术方案进行评估,验证技术方案是否能满足设计目标。测试左移还可以提前准备用例和测试环境,调整测试方案以具备更好的测试性。
测试右移是指软件进入运营阶段后也需要 QA 参与,软件发布后 QA 需要持续关注线上预警和监控,及时发现问题,并尝试在测试环境中重现。这样 QA 就可以驱动研发人员在开发过程中考虑接入监控、告警等基础设施,开发团队需要比市场、业务方以及用户更快地发现问题,并制定解决措施。
7、质量度量
度量可以通过用量化的方法取代定性的结论,来评估软件的质量、过程和测试有效性等,作为合理决策的依据。对于人员分配、能效提升、绩效考核等各个方面都有一定的意义。
质量度量的指标研究包含以下几个方面:
- 对于产品的质量进行度量。
- 对于测试的有效性进行度量。
- 对于测试的完整性进行度量。
- 对于测试过程和软件开发过程的分析和改进。
对于普通测试人员和研发人员来说,不需要特别去设计这些度量指标,可以根据国际、国内的指标标准提取一些合适自己的指标,作为公司内部使用的度量体系。
ISO/IEC 9126 标准从功能性、可靠性、易用性、效率、可维护性、可移植性这 6 个特性进行度量,和前面对缺陷的理解类似,ISO/IEC 9126 标准将这些指标划分为通用、内部指标、外部指标。内部指标的含义是,侧重在交付前的度量,不关注缺陷被激活的情况,更加关注软件的本质问题。
在国内也有相应的度量体系,GB/T 延用了 ISO/IEC 9126 中的指标,从 6 个方面对软件质量进行评估。在 《GB/T 32904—2016 软件质量量化评价规范》中,该文档给出了一套根据指标计算软件质量的方法,其中参考使用的标准体系如下图所示。
对于测试有效性和完整性的衡量可以参考一些主流软件公司的做法,比如某公司会根据千行代码(代码规模一般不作为唯一手段)为基线统计一些测试能力的指标:
- 每千行用例数。
- 每千行缺陷率。
- 用例平均执行时间。
- 缺陷平均回归次数。
- 有效缺陷率(经过确认特性描述不清楚导致的缺陷)。
通过度量后,会根据缺陷的优先级和严重程度进行加权计算,获得每个版本的软件质量指数。
在应用指标的时候,还需要做出区分。由于有一些项目是建立在遗留系统之上的,它的开发过程、逻辑和完全从头开始的项目很不一样。因此我们在使用这些指标的时候,可以根据项目的类型做出取舍。根据软件系统遗留特性可以将项目划分为绿地工程、棕地工程、维护性工程。
- 绿地工程:一项绿地工程在全新的领域中开发,不需要考虑历史遗留问题。绿地工程往往存在需求无法清晰描述特性,有效缺陷率低,缺陷修复的成本低,缺陷回归的效率高等特点。
- 棕地工程:此类工程中的系统通常是现有系统的一部分,或者是其子系统。需要考虑与其他系统尤其是与历史遗留系统(Legacy system)的集成问题。这类项目往往缺陷回归次数高,缺陷的修复成本很大。
- 维护性工程:指的是不再开发新功能的系统,只完成维护性工作。
二、软件的测试分类
不同维度下人们对软件测试的分类不一致。比如,根据开发过程进行分类,软件的测试类型有:单元测试、集成测试、系统测试和验收测试,这些类型分别和软件开发过程中的各个阶段相适配。但是如果从被测试的对象角度来看,软件测试又可以分为静态测试和动态测试。如果是以测试人员对代码的了解程度来看,则可以分为白盒测试、黑盒测试、灰盒测试。根据是否是自动化运行的测试来区分,又可以分为自动化测试和手工测试。
还有一些其他的测试类型,比如契约测试、弹珠测试、冒烟测试等。根据前面提到的测试策略和测试金字塔,并参考 GB/T 的规范,对一个敏捷团队需要进行的测试类型进行简化。为了更加容易理解和记忆,将其分为功能性测试和非功能性测试。
功能性测试对应的是功能性的需求,针对软件特性的业务目标和逻辑。非功能性测试,针对的是功能测试来说的,即功能测试之外的要求。
功能性测试
- 单元测试。
- 集成测试。
- 系统测试。
- 验收测试。
非功能性测试
- 静态分析。
- 安全测试。
- 性能测试。
就一般的软件而言,一个敏捷团队做好这 7 项测试就能涵盖绝大部分测试需求。下面就这 7 类测试进行简单的介绍。
1、单元测试
单元测试(Unit Testing)是指对软件中最小可测试单元进行测试。“单元” 的粒度并没有一个明确的界限,细粒度的单元测试含义一般是对方法、类等代码结构进行测试和验证。粗粒度的单元测试可以是对一个最小的软件特性进行验证,根据设计需求文档或者设计文档进行验证。
单元测试往往处于测试金字塔的最低端。因为单元测试能透明地验证方法、类这一类代码结构,因此编写自动化运行的测试也比较简单。在目前的语义下,单元测试默认有自动化运行的含义。
对软件质量来说,单元测试有非常积极的作用,是测试金字塔中最重要的部分。通过单元测试可以将复杂的测试用例进行拆解,比如从较大规模的测试路径分解成小规模的测试。且单元测试的难度相对较小,测试效率也相应较高。单元测试对环境要求低,隔离性好,为同时运行多个测试用例提供了可能性。
另外,研发人员编写单元测试,在遇到问题时可以帮助定位缺陷。
2、集成测试
集成测试(Integration Testing)是指在单元测试的基础上,对一部分软件模块进行组合或者在组装后进行的测试。在微服务时代,通常来说集成测试是对一个服务的 API 进行测试,因此在很多文章和书籍中都会有 API 测试。请注意 API 这个词的含义过于广泛,包括 RESTful API 和操作系统等软件接口的概念,需要根据上下文确定含义。在微服务的技术栈下,通常集成测试等同于单个服务的 API 测试。
随着 DevOps 的发展,持续集成的概念得到了广泛关注。
在一些大的项目中,服务众多且相互依赖。早期的集成测试大多是手工完成的,会在集成测试时,先进行一个快速地冒烟测试(Smoke Testing)。冒烟测试是对软件的基本功能进行快速验证的过程。冒烟测试用来检查主要的功能是否正常,避免因为其中一个服务异常而对整套测试环境造成中断。
在 CI/CD 发展比较好的团队,会在服务部署到正式的测试环境前,对 API 进行自动化的测试,如果测试发现问题,会停止部署到测试环境。集成测试的依据是技术设计文档,比如 API 设计等材料。
一般情况下,研发人员会参与集成测试,或者是在研发人员的配合下由 QA 完成集成测试。
3、系统测试
系统测试(System Testing)是指对完整的软件产品进行端到端的测试,某种程度上可以等同于 E2E测试。系统测试需要搭建完整的环境,以便在真实或者模拟系统的环境下对软件进行验证,确认是否达到设计目标。需要配置的完整的软硬件环境和基础设施包括数据库、网络连接、DNS 等。
系统测试往往是黑盒测试,此测试会模拟正常的用户在使用整个应用程序时的各种操作。系统测试不仅要发现缺陷,还应该提出不限于需求规格范围的反馈,甚至还包括一些使用过程中的易用性、兼容性等问题。
系统测试的依据是需求文档,包括 QA 在需求澄清阶段的任何输入。
4、验收测试
验收测试(Acceptance Testing)是指在软件开发后期,需求的提出方对软件进行验收确认时进行的测试。如果是软件交付性质的项目,甲方往往会派出测试专家,从用户的角度进行测试,确认是否满足需求规格。在互联网或者其他产品型的公司,验收测试则是产品上线或者软件发布后,由业务方在生产环境(或灰度环境)下进行验证。
对一个在运行中的互联网产品来说,验收测试需要得到特别的授权。因为在生产环境中,一般会产生数据或者留下痕迹,在有条件的情况下可以考虑清理或者隐藏这些信息。
5、静态分析
软件的静态分析(Static Analysis)是指对软件的各种结构和成分进行扫描,提前发现问题,它通常被作为测试的补充。静态分析一般都是用自动化的工具或者平台在日常进行扫描和监测的。
静态分析的目的就是通过扫描的手段发现代码中的一些通用问题,或者找出违反编码、安全规范的代码。常见的扫描工具如下。
- Checkstyle:Java 代码语法规则扫描。
- FindBugs:从代码模式上发现潜在问题。
- ArchUnit:架构规范扫描,验证软件包的组织合理性。
- OWASP Dependency-Track:对依赖的第三方软件和库进行检查,发现是否存在安全风险。
除此之外,市面上还有一些其他的扫描工具,比如:PMD、FortifySCA 这些都属于静态分析的内容。
6、安全测试
安全测试(Security Testing)是指对系统的安全要求进行验证的一类测试。安全测试针对的是代码执行、命令执行、病毒植入、端口扫描、DoS 攻击、SQL 注入攻击、CSRF 攻击、XSS 攻击、数据遍历、越权、认证绕过、金额篡改等安全问题的测试。
由于互联网项目会将用户信息暴露到公网,近些年来企业对安全测试的要求又有一定提高,数据隐私、威胁建模也都被包含在安全测试的领域。
- 数据隐私是指软件系统在使用用户的信息时,需要满足当地法律合规的要求,且应尽力保护用户的隐私数据。
- 威胁建模是通过一些建模工具来分析软件会受到哪些方面的安全威胁,并制定测试策略的过程。STRIDE 是常用的威胁模型,它包含欺骗、篡改、否认、信息泄露、DoS威胁、特权提升 6 个方面,从这些方面可以结构化地制定应对措施。
在一些大的团队中,安全测试往往会由专门的安全专家进行或者给予指导。没有条件的,则由研发人员和 QA 共同完成。
7、性能测试
性能测试(Performance Testing)是指针对软件性能指标进行的测试。性能测试包括了软件响应速度和用户容量等方面的内容。
响应速度代表用户使用软件的等待时间,对于一些常规操作而言,等待时间过长,会极大地影响用户使用。
用户容量代表着有多少用户能同时使用该软件。单机软件对用户容量要求不高,对于互联网项目,性能测试需要涵盖容量指标。性能测试中有一种负载测试,用于通过模拟用户递增的方式找出系统的最大容量,以及验证系统是否能通过增加服务的方式水平扩容。
性能测试工具包括 JMeter、AB、K6 等。JMeter、AB 都是 Apache 基金会的产品,具有良好的使用口碑。K6 是一款新的性能测试工具,能使用 JavaScript 语法编写自动化的性能测试脚本,对 Web 程序相当友好。