测试的类型
常见的测试类型主要有以下几种:
- 单元测试:验证独立单元是否能正常工作
- 集成测试:验证多个单元协同工作
- 端到端测试:从用户角度以机器的方式在真实浏览器环境验证应用交互
- 快照测试:验证程序的UI变化
单元测试
单元测试指的是对应用程序中的最小单元进行测试,一般包括函数、组件等。
以下是一个单元测试例子,只要输入参数后的计算结果与预期结果不一致测试程序就会抛出错误。
函数代码:
// count.js
const count = (a,b)=>{
return a+b
}
单元测试的代码:
// test.space.js
const testCount = ()=>{
if(count(1,1) !== 2){
throw new Error('count(1,1) did not return 2')
}
}
优点:
- 提升代码质量,减少BUG
- 快速反馈,减少调试时间
- 让代码维护更容易
- 有助于代码的模块化设计
- 代码覆盖率高
缺点:
- 单元测试是独立的,所以无法保证多个单元运行在一起是否正确
常见的单元测试框架:
- Jest
- Mocha
- ...
目前最流行的是Jest,它功能齐全、配置方便。还有就是Mocha相对配置更灵活。推荐使用Jest。
现在我也找了很多测试的朋友,做了一个技术分享的交流群,共享了很多我们收集的视频教程和技术文档。如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受,可以加入我们一起交流。而且还有很多在自动化,性能方面有一定建树的技术大牛,分享他们的经验,还会分享很多直播讲座和技术沙龙,可以免费学习!划重点!开源的!!!
qq群号:110685036
集成测试
多个模块或组件集成进行测试(或者说多个单元组合进行的测试)称之为集成测试。 单元测试是独立的,无法保证多个单元运行在一起的正确性。而集成测试就却能解决这一问题。
集成测试主要从用户角度出发,不需要关注代码内部的实现逻辑,只专注于呈现正确的结果
优点:
- 从用户使用角度出发,更容易获得软件使用过程中的正确性
- 集成测试相当于写了软件的说明文档
- 不关注底层代码试下细节,更有利于快手重构
- 相比单元测试,开发速度要更快
缺点:
- 测试失败时,无法快速定位问题
- 代码覆盖率低
- 速度比单元测试要慢
端到端测试(E2E)
E2E(end to end)端到端测试是最直观可以理解的测试类型。从用户的视角通过浏览器自动检查应用程序是否正常工作。
与单元测试、集成测试不同的是,e2e是在真实的浏览器环境中进行自动测试。模拟输入测试交互结果。
优点:
- 在真实的浏览器中进行运行测试,测试与运行结果更真实
缺点:
- 耗时较久,需要启动浏览器与网站响应。一整套测试下来可能30分钟~几小时
- 测试失败时,无法快速精准定位问题
- 调试困难,要调试端到端测试,需要打开浏览器逐步完成用户操作以重现BUG。如果是在持续集成服务器上失败,那么整个调试过程将会变成更复杂。
端到端测试框架:
- Cypress - 较为流行
- NightWatch
- WebdriverIO
- playwright - 微软
快照测试
可以检测组件的结构改动,首次执行时会储存组件快照,将结果以文本的形式进行储存。后续进行测试时会进行比较。适合开发组件库时使用,像一些第三方组件库就会经常使用快照测试。
测试金字塔
Mike Cohn 在他的著作一书《Succeeding with Agile》中的概念 单元测试、集成测试、快照测试、端到端测试我们都需要按情况进行编写,而测试金字塔的概念是,测试是需要分层的,每层需要写多少测试。
自上而下分别为单元、集成、UI测试,最底下单元测试成本对最低,与之相对的UI测试成本最高。所以单元测试写的数量最多,UI测试写的数量最少。越是上层的测试,其通过率给开发者带来的信心就越大。
奖杯模型
奖杯模型自上而下将测试划分为静态测试、单元测试、集成测试、e2e测试,其中集成测试占据大部分。
- 静态测试:在编写代码逻辑阶段时进行报错提示。(代表库:ESlint、Flow、TypeScript)
- 单元测试:奖杯模型中,单元测试的职责是对一些边界情况或者特定的算法进行测试(代表库:Jest、Mocha)
- 集成测试:模拟用户的行为进行测试,对网络请求、获取数据库的数据等依赖第三方环境的行为进行Mock。(代表库:Jest、react-testing-library、Vue Testing Library等)
- e2e测试:模拟用户在真实环境上操作行为,包括网络请求、获取数据库数据等的测试。(代表库:Cypress) 奖杯模型 越是上层的测试给开发者带来的信心是越大的,与此同时,越是下层的测试的效率是越高的,奖杯模型综合考虑了这两点,可以看到其在集成测试中的占比是最高的。
为了维持奖杯模型的形状,一个健康、快速、可维护的测试组合应该是这样的:
- 在底层为应用配置静态测试,比如使用ESlint约束代码规范、使用TypeScript增强类型定义
- 为应用中的特定算法或者工具函数编写小二快的单元测试
- 为稳定组件而编写快照测试
- 写许多模拟真实用户行为的集成测试,增强应用构建信心
- 为应用核心业务流程编写少量的高层次端到端测试
下面是针对不同应用场景的一些建议:
- 开发纯函数库,建议主要写单元测试 + 少量集成测试
- 如果开发组件库,建议主要写快照测试 + 少量集成测试
- 开发业务系统,建议都安排上,算法、函数编写单元测试、组件编写快照测试、为核心业务编写少量的e2e测试
测试覆盖率
测试覆盖率是衡量软件测试完整性的一个重要指标。掌握测试覆盖率数据,有利于客观认识软件质量,正确了解测试状态,有效改进测试工作。
如何度量测试覆盖率?
- 代码覆盖率
- 需求覆盖率
代码覆盖率
在执行代码测试时,有那些软件代码被执行到了,有那些软件代码没有被执行到。被执行代码数量与总代码数量之间的比值,就是代码覆盖率。
根据代码粒度,还能进行如下划分,他们形式各异,但本质是相同的。
- 源文件覆盖率
- 类覆盖率
- 函数覆盖率
- 分支覆盖率
- 语句覆盖率
- ...
第三方工具中一般自带了测试覆盖率,如 Jest。但是他们一般只适用于白盒测试(知道代码的),尤其是单元测试。对于黑盒测试(例如功能测试/系统测试)来说,度量他们的代码覆盖率则相对困难多了。
需求覆盖率
对于黑盒测试,例如功能测试、集成测试、系统测试等,测试用例通常是基于软件需求而不是软件实现所设计的。因此度量这类测试完整性的手段一般是需求覆盖率,即测试所覆盖的需求数量与总需求数量的比值。
没有现成的第三方工具能度量需求覆盖率。而需要依赖于人工计算,尤其是需要依赖人工去标记每个测试用例和需求直接的映射关系。
总结
代码覆盖率和需求覆盖率是相互补充的,他们使用的场景不同,有各自的优势与不足。
测试覆盖率还需要结合成本,对于一个完整的项目,建议覆盖率先做到80%的测试用例,后期再慢慢完善。而对于经常不回变更的公共函数方法尽可能的做到100%的覆盖率。
例如一些经常做改动的需求页面,我们没必要让覆盖率必须趋近与100%,因为需求不断的变更,测试用例也需要不断的变更,维护成本就太高了。
大多数情况下,将100%的代码覆盖率作为目标并没有意义,不仅耗时耗力,即使达到了100%覆盖率的测试也并非总能发现BUG,有时你可能还会做出错误的假设,比如调用API,但是测试时接口永远没有返回错误,然而在生产环境时API却产生了错误,此时应用就出BUG了,而100%的覆盖率却没有检查到该问题。
当然如果是开发极其重要类似支付应用的,那么 100% 的代码覆盖率的成本是值得付出的。
测试开发的方式
- TDD 测试驱动开发,先写测试后实现功能
- BDD 行为驱动开发,先实现功能再写测试
TDD测试驱动开发
TDD(Test-driven development),测试驱动开发,是敏捷开发中的一项核心实践和技术,也是一种软件设计方法论。
TDD开发流程:
- 编写测试用例
- 运行测试
- 编写代码使测试通过
- 重构、优化代码
- 新增功能,重复上诉步骤
TDD 测试原则:
- 独立测试 - 不同代码的测试应该互相独立,一个类对应一个测试类,一个函数对应一个测试函数。每个用例不能使用其他用例的结果数据,结果也不能依赖于用例执行的顺序。一个角色:开发过程包含多种工作,如:编写测试代码、编写产品代码、代码重构等。做不同的工作时,应专注于当前的角色,不要过多考虑其他方面的细节。
- 测试列表 - 代码的功能点可能很多,并且需求可能陆续出现,任何阶段添加功能时,应把相关功能点加到测试列表中,然后才继续手头工作,避免疏漏。
- 测试驱动 - 即利用测试来驱动开发,是TDD的核心。要实现某个功能,要编写某个类或某个函数,应首先编写测试代码,明确这个类、这个函数如何使用,如何测试,然后在对其进行设计、编码。
- 先写断言 - 编写测试代码时,应首先编写判断代码的断言语句,然后再编写必要的辅助语句。
- 可测试下 - 代码设计、开发时应尽可能提高可测试性。如模块化、组件化开发。每个代码单元的功能应比较单纯(各家自扫门前雪),每个类、函数应该只做它该做的事情,不要弄成大杂烩。尤其是添加新功能时,不要为了图一时之便,随便在源代码中添加功能。
- 及时重构 - 对结构不合理、重复等不好的代码,在测试通过后,应及时进行重构。
- 小步前进 - 软件开发是复杂性非常高的工作,小步前进是降低复杂度的好办法。
TDD 的优点:
- 保证代码质量,因为先编写测试,所以可能出现的问题都被提前发现了
- 促进开发人员思考,有利于程序模块测试
- 测试覆盖率高,因为先编写测试代码,后编写代码,所以测试用例基本都能照顾到
TDD 的缺点:
- 代码量增多,大多数情况下测试代码时功能代码的两倍甚至更多
- 业务耦合度高,测试用例中使用了业务中一些模拟的数据,当业务代码变更时,要去重新组织测试用例。
- 关注点过于独立,由于单元测试只关注这一个单元的状况,无法保证多个单元组合的整体是否正常。
在前端应用实际开发过程中 TDD 更适合开发纯函数库,比如:Lodash、Vue、React等。
BDD行为驱动开发
BDD(Behavior=driven development)行为驱动开发,是测试驱动开发延伸出来的一种敏捷软件开发技术。
TDD 最大的一个问题是在于开发人员最终做出来的东西和实际功能需求想偏离,为了解决这一问题有人发明了BDD。
BDD 解决的另外一个关键问题就是如何定义TDD或者单元测试过程中的细节。一些不良的单元测试的常见问题是过于依赖被测试功能的实现逻辑。 这通常意味着如果你要修改实现逻辑,及时输入输出没有变,通常也需要去更新测试代码。这就造成了一个问题,让开发人员对测试代码的维护感觉乏味和厌烦。
- BDD 核心墓地是为了解决TDD模式下开发和实际功能需求不一致而诞生的
- BDD 不需要再面向实现细节设计测试,取而代之的是面向行为来测试
- 它是从产品角度出发,鼓励开发人员和非开发人员(产品、QA、客户等)之间的协作
- 由于BDD 的核心是关注软件的功能测试,所以BDD更多的是结合集成测试进行,他是黑盒的
BDD 的发开流程大致是:
- 开发人员和非开发人员一起讨论确认需求
- 以一种自动化的方式将需求建立起来,并确认是否一致
- 最后,实现每个文档实例描述的行为,并从自动化测试开始以指导代码的开发。
理想中的BDD解决方案最流行的是 Cucumber。它的协作流程是这样的:
- 开发人员与产品、测试、客户等人员沟通确认需求
- 使用同一的 GherKin 语法将功能需求转换为需求文档,应由非开发人员进行编写(产品、测试、客户)
用描述性自然语言定义的测试,客户、测试人员和开发人员都能看得懂,能达成共识,这种语法叫做 Gherkin Syntax,小黄瓜语法。
以关键字 Scenario、Feature 等描述场景 以关键字 Given、When、Then 来描述步骤
Feature: 添加任务(新增功能的描述 )
Scenario: 在输入框中输入任务名称敲回车确定,输出到任务列表中
Given "下午四点开周会"
When 在输入框中敲回车
Then 在任务列表增加一个名称为 "下午四点开周会" 的任务
Scenario: 在输入框中输入空内容,不输出到任务列表中
Given ""
When 在输入框中敲回车
Then 在任务列表中不增加任何内容
- 开发人员根据 Gherkin 编写测试用例
- 编写代码使测试通过
- 新增功能重复上诉步骤
BDD 注重的是产品的功能,可能无法保证很好的代码质量和测试覆盖率,所以还有人提出了一种 BDD + TDD 的方案。
开发软件的时候,先以 BDD 的流程开始,讨论需求,指定需求文档,然后开发人员将需求文档转化成测试用例,此时不急着实现功能。
- 需求分析
- 描述需求定义文档
- 编写集成测试用例
- 接着执行 TDD 流程
- 编写单元测试用例
- 编写代码使单元测试通过
- 重构优化
- 等到 TDD 单元测试全部完成,再实现 BDD 功能测试用例。
- 增加功能重复上述步骤。
这样既保证了功能和用户实际需求,也保证了代码的质量。
这样可以把 BDD 看作是在需求与 TDD 之间架起一座桥梁,它将需求进一步场景化,更具体的描述系统应该满足哪些行为和场景,让 TDD 的输入更优雅、更可靠。
当然实际场景中,这个方案要写大量测试代码,所以它也是一个理想中的方案,实施起来还是有一定的困难。
还有一种更轻量的 BDD 方案就是以集成测试为主的开发方案:
- 需求分析
- 编写集成测试用例(开发人员编写集成测试用例,无需再编写Gherkin需求文档)
- 运行测试
- 代码实现使测试通过
- 重构优化
- 新增功能重复上诉步骤
BDD 的优点:
- 由于侧重于需求功能的完整度,所以给开发人员增加更多对程序的信心
- 由于仅关注功能,不关注实现细节,有利于测试代码和实际代码解耦
- 由于大多数为编写集成测试,相比TDD有更好的开发效率
BDD 的缺点:
- 因为以功能性的集成测试为主,因此不是那么关注每个函数的功能,测试覆盖率比较低
- 没有TDD那么严谨的保证代码质量
权衡利弊
当我们开始编写自动化测试时,可能想要测试所有的东西。因为我亲身经历了未经测试的应用程序带来的痛苦。我不想再体验同样的经历,但很快我又学到了另一课 - 测试会减缓开发速度。
在编写测试时,请务必牢记编写测试的目的。通常,测试的目的是为了节省时间。如果你正在进行的项目是稳定的并且会长期开发,那么测试是可以带来收益的。
但是如果测试编写与维护的时间长于他们可以节省的时间,那么你根本不应该编写测试。当然,在编写代码之前你很难知道通过测试可以节省多少时间,你会随着时间的推移去了解。但是,假设你正在一个短期项目中创建原型,或者在一个创业公司迭代一个想法,那你可能不会从编写测试中获得收益。
凡是都有两面性,软件测试也不是银弹,好处虽然明显,却并不是所有的项目都值得引入测试框架,毕竟维护测试用例也是需要成本的。对于一些需求频繁变更、复用性较低的内容,比如活动页,让开发专门抽出人力来写测试用例确实得不偿失。
而适合引入测试场景的大概有那么几个:
- 需要长期维护的项目,它们需要测试来保障代码可维护性、功能的稳定性
- 较为稳定的项目、或项目中较为稳定的部分。给他们写测试用例,维护成本低
- 被多次复用的部分,比如一些通用组件和函数。因为多处复用,更需要保障质量
测试确实会带给你相当多的好处,但不是立刻体验出来。就如买重疾保险,交了很多保费,没病没痛,十几年甚至几十年都用不上,最好就是一辈子用不上理赔,身体健康最重要。测试也一样,写了可以买个放心,对代码的一种保障,有 bug 尽快测出来,没 bug 就最好,但不能说“写那么多测试,结果测不出 bug,浪费时间”吧。
一些术语解释
- 黑盒测试:不考虑程序内部结构和逻辑结构,主要是测试系统的功能是否满足“需求规格说明书”。一般会有一个输入值和一个输出值,和期望值做比较。黑盒测试也被称为功能测试或数据驱动测试,它是通过测试来检测每个功能是否都能正常使用。
- 白盒测试:主要应用于单元测试阶段,主要是对代码级别的测试,针对程序内部的逻辑结构。测试的手段有:语句覆盖、判定覆盖、条件覆盖、路径覆盖和条件组合覆盖。
END配套学习资源分享
最后: 为了回馈铁杆粉丝们,我给大家整理了完整的软件测试视频学习教程,朋友们如果需要可以自行免费领取 【保证100%免费】
加入我的软件测试交流群:110685036免费获取~(同行大佬一起学术交流,每晚都有大佬直播分享技术知识点)
软件测试面试小程序
被百万人刷爆的软件测试题库!!!谁用谁知道!!!全网最全面试刷题小程序,手机就可以刷题,地铁上公交上,卷起来!
涵盖以下这些面试题板块:
1、软件测试基础理论 ,2、web,app,接口功能测试 ,3、网络 ,4、数据库 ,5、linux
6、web,app,接口自动化 ,7、性能测试 ,8、编程基础,9、hr面试题 ,10、开放性测试题,11、安全测试,12、计算机基础
获取方式 :