《软件测试52讲》学习笔记(二)

因为报了一个课程,记录一些自己觉得有用的东西,课程链接:https://time.geekbang.org/column/article/10150

 

二、测试基础知识篇

  • 第四小节  为什么要做自动化测试?什么样的项目适合做自动化测试?

1、什么是自动化测试?

    自动化测试是,把人对软件的测试行为转化为由机器执行测试行为的一种实践,自动化测试的本质是先写一段代码,然后去测试另一段代码,所以实现自动化测试用例本身属于开发工作,需要投入大量的时间和精力,并且已经开发完成的用例还必须随着被测对象的改变而不断更新,你还需要为此付出维护测试用例的成本。当你发现自动化测试用例的维护成本高于其节省的测试成本时,自动化测试就失去了价值与意义,你也就需要在是否使用自动化测试上权衡取舍了。

2、为什么需要自动化测试?

  优势:

  • 自动化测试可以替代大量的手工机械重复性操作,测试工程师可以把更多的时间花在更全面的用例设计和新功能的测试上;
  • 自动化测试可以大幅提升回归测试的效率,非常适合敏捷开发过程;
  • 自动化测试可以更好地利用无人值守时间,去更频繁地执行测试,特别适合现在非工作时间执行测试,工作时间分析失败用例的工作模式;
  • 自动化测试可以高效实现某些手工测试无法完成或者代价巨大的测试类型,比如关键业务 7×24 小时持续运行的系统稳定性测试和高并发场景的压力测试等;
  • 自动化测试还可以保证每次测试执行的操作以及验证的一致性和可重复性,避免人为的遗漏或疏忽。

  劣势:

  • 自动化测试并不能取代手工测试,它只能替代手工测试中执行频率高、机械化的重复步骤。你千万不要奢望所有的测试都自动化,否则一定会得不偿失;
  • 自动测试远比手动测试脆弱,无法应对被测系统的变化,业界一直有句玩笑话“开发手一抖,自动化测试忙一宿”,这也从侧面反映了自动化测试用例的维护成本一直居高不下的事实。其根本原因在于自动化测试本身不具有任何“智能”,只是按部就班地执行事先定义好的测试步骤并验证测试结果。对于执行过程中出现的明显错误和意外事件,自动化测试没有任何处理能力;
  • 自动化测试用例的开发工作量远大于单次的手工测试,所以只有当开发完成的测试用例的有效执行次数大于等于 5 次时,才能收回自动化测试的成本;
  • 手工测试发现的缺陷数量通常比自动化测试要更多,并且自动化测试仅仅能发现回归测试范围的缺陷;
  • 测试的效率很大程度上依赖自动化测试用例的设计以及实现质量,不稳定的自动化测试用例实现比没有自动化更糟糕;
  • 实行自动化测试的初期,用例开发效率通常都很低,大量初期开发的用例通常会在整个自动化测试体系成熟,和测试工程师全面掌握测试工具后,需要重构;
  • 业务测试专家和自动化测试专家通常是两批人,前者懂业务不懂自动化技术,后者懂自动化技术但不懂业务,只有二者紧密合作,才能高效开展自动化测试;
  • 自动化测试开发人员必须具备一定的编程能力,这对传统的手工测试工程师会是一个挑战。

3、什么样的项目适合自动化测试?

3.1、第一,需求稳定,不会频繁变更;

3.2、第二,研发和维护周期长,需要频繁执行回归测试;

  • 在我看来,软件产品比软件项目更适合做自动化测试
  • 对于软件项目的自动化测试,就要看项目的具体情况了

3.3、第三,需要在多种平台上重复运行相同测试的场景;

3.4、第四,某些测试项目通过手工测试无法实现,或者手工成本太高;

3.5、第五,被测软件的开发较为规范,能够保证系统的可测试性;

另外,某些用例的自动化必须要求开发人员在产品中预留可测试性接口,否则后续的自动化会很难开展。

3.6、第六,测试人员已经具备一定的编程能力;

4、总结

自动化测试是,把人工对软件的测试转化为由机器执行测试行为的一种实践,可以把测试工程师从机械重复的测试工作中解脱出来,将更多的精力放在新功能的测试和更全面的测试用例设计上。

然而自动化测试试一把“双刃剑”,虽然它可以从一定程度上解放测试工程师的劳动力,完成一些人工无法实现的测试,但并不适用于所有的测试场景,如果维护自动化测试的代价高过了节省的测试成本,那么在这样的项目中推进自动化测试就会得不偿失。


  • 第五小节  你知道软件开发各阶段都有哪些自动化测试技术吗?

1、单元测试的自动化技术

      在软件研发生命周期的各个阶段都有自动化测试技术的存在,并且对提升测试效率有着至关重要的作用。

1、从广义上讲,单元测试阶段的“自动化”内涵不仅仅指测试用例执行的自动化,还应该包含以下五个方面:

1.1、用例框架代码生成的自动化;

1.2、部分测试输入数据的自动化生成;

1.3、自动桩代码的生成;

      简单地说,桩代码(stub code)是用来代替真实代码的临时代码。 比如,某个函数 A 的内部实现中调用了一个尚未实现的函数 B,为了对函数 A 的逻辑进行测试,那么就需要模拟一个函数 B,这个模拟的函数 B 实现就是所谓的桩代码。

1.4、被测代码的自动化静态分析;

1.5、测试覆盖率的自动统计与分析;

2、代码级集成测试的自动化技术

代码级集成测试与单元测试最大的区别只是,代码级集成测试中被测函数内部调用的其他函数必须是真实的,不允许使用桩代码代替,而单元测试中允许使用桩代码来模拟内部调用的其他函数。

3、Web Service 测试的自动化技术

Web Service 测试,主要是指 SOAP API 和 REST API 这两类 API 测试,最典型的是采用 SoapUI 或 Postman 等类似的工具。但这类测试工具基本都是界面操作手动发起 Request 并验证 Response,所以难以和 CI/CD 集成,于是就出现了 API 自动化测试框架。

3.1、对于基于代码的 API 测试用例,通常包含三大步骤:

  • 准备 API 调用时需要的测试数据;
  • 准备 API 的调用参数并发起 API 的调用;
  • 验证 API 调用的返回结果。

3.2、Web Service 测试“自动化”的内涵不仅仅包括 API 测试用例执行的自动化,还包括以下四个方面:

  • 测试脚手架代码的自动化生成;

和单元测试阶段的用例框架代码自动生成一个道理,你在开发 API 测试的过程中更关心的是,如何设计测试用例的输入参数以及组合,以及在不同参数组合情况下 Response 的验证,而你不希望将精力浪费在代码层面如何组织测试用例、测试数据驱动如何实现等非测试业务上。这时,测试脚手架代码的自动生成技术就派上用场了。它生成的测试脚手架代码,通常包含了被测试 API 的调用、测试数据与脚本的分离,以及 Response 验证的空实现。

  • 部分测试输入数据的自动生成;

这一点和单元测试的测试输入数据的自动化生成也很类似,唯一不同的是,单元测试针对的参数是函数输入参数和函数内部输入,而 API 测试对应的是 API 的参数以及 API 调用的 Payload。数据生成的原则同样遵循边界值原则

  • Response 验证的自动化;

对于 API 调用返回结果的验证,通常关注的点是返回状态码(status code)、Scheme 结构以及具体的字段值。如果你写过这种类型的测试用例,那你就会知道字段值的验证相当麻烦,只有那些你明确写了 assert 的字段才会被验证,但是通常你不可能针对所有的字段都写 assert,这时就需要 Response 验证的自动化技术了。Response 验证自动化的核心思想是自动比较两次相同 API 调用的返回结果,并自动识别出有差异的字段值,比较过程可以通过规则配置去掉诸如时间戳、会话 ID(Session ID)等动态值。 这部分内容,我会在后续文章中详细讲解。

  • 基于 SoapUI 或者 Postman 的自动化脚本生成;

你在使用 SoapUI 或者 Postman 等工具进行 Web Service 测试时,已经在这些工具里面积累了很多测试用例。那么,在引入了基于代码实现的 API 测试框架之后,就意味着需要把这些测试用例都用代码的方式重写一遍,而这额外的工作量是很难被接受的。我的建议是,开发一个自动化代码转换生成工具。这个工具的输入是 SoapUI 或者 Postman 的测试用例元数据(即测试用例的 JSON 元文件),输出是符合 API 测试框架规范的基于代码实现的测试用例。这样一来,原本的测试用例积累可以直接转换成在 CI/CD 上可以直接接入的自动化测试用例。对于新的测试用例,还可以继续用 SoapUI 或者 Postman 做初步的测试验证,初步验证没有问题后,直接转换成符合 API 测试框架规范的测试用例。对于复杂的测试用例,也可以直接基于代码来实现,而且灵活性会更好。

4、GUI测试的自动化技术

目前,GUI 自动化测试主要分为两大方向,传统 Web 浏览器和移动端原生应用(Native App)的 GUI 自动化。虽然二者采用的具体技术差别很大,但是用例设计的思路类似。

  • 对于传统 Web 浏览器的 GUI 自动化测试,业内主流的开源方案采用 Selenium,商业方案采用 Micro Focus 的 UFT(前身是 HP 的 QTP);
  • 对于移动端原生应用,通常采用主流的 Appium,它对 iOS 环境集成了 XCUITest,对 Android 环境集成了 UIAutomator 和 Espresso。

5、总结

我给你梳理了软件研发生命周期各个阶段的自动化测试技术,包括单元测试、代码级集成测试、Web Service 测试和 GUI 测试的自动化技术,并给你归纳了每一类技术的核心方法和应用场景。

我希望你通过这篇文章,可以先对自动化测试的全局有一个比较清晰的认识,然后在后续的文章中我还会针对这些技术展开讨论,并给你分享一些相应的实际案例。


第六小节  你真的懂测试覆盖率吗?

测试覆盖率通常被用来衡量测试的充分性和完整性,从广义的角度来讲,测试覆盖率主要分为两大类,一类是面向项目的需求覆盖率,另一类是更偏向技术的代码覆盖率。

1、需求覆盖

率需求覆盖率是指测试对需求的覆盖程度,通常的做法是将每一条分解后的软件需求和对应的测试建立一对多的映射关系,最终目标是保证测试可以覆盖每个需求,以保证软件产品的质量。

2、代码覆盖率

  • 简单来说,代码覆盖率是指,至少被执行了一次的条目数占整个条目数的百分比;
  • 如果“条目数”是语句,对应的就是代码行覆盖率;如果“条目数”是函数,对应的就是函数覆盖率;如果“条目数”是路径,那么对应的就是路径覆盖率。依此类推,你就可以得到绝大多数常见的代码覆盖率类型的定义;

2.1、三种常用的覆盖率概念

  • 行覆盖率又称为语句覆盖率,指已经被执行到的语句占总可执行语句(不包含类似 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 各一次;

3、代码覆盖率的价值

现在很多项目都在单元测试以及集成测试阶段统计代码覆盖率,但是我想说的是,统计代码覆盖率仅仅是手段,你必须透过现象看到事物的本质,才能从根本上保证软件整体的质量。

统计代码覆盖率的根本目的是找出潜在的遗漏测试用例,并有针对性的进行补充,同时还可以识别出代码中那些由于需求变更等原因造成的不可达的废弃代码。

4、代码覆盖率的局限性

总结来讲,高的代码覆盖率不一定能保证软件的质量,但是低的代码覆盖率一定不能能保证软件的质量。

5、代码覆盖率工具

JaCoCo 是一款 Java 代码的主流开源覆盖率工具,可以很方便地嵌入到 Ant、Maven 中,并且和很多主流的持续集成工具以及代码静态检查工具,比如 Jekins 和 Sonar 等,都有很好的集成。

1、JaCoCo 代码覆盖率统计报告实例

图中绿色的行表示已经被覆盖,红色的行表示尚未被覆盖,黄色的行表示部分覆盖;左侧绿色菱形块表示该分支已经被完全覆盖、黄色菱形块表示该分支仅被部分覆盖。

6、代码覆盖率工具的实现原理

上图为统计代码覆盖率的不同注入实现技术

实现代码覆盖率的统计,最基本的方法就是注入(Instrumentation)。简单地说,注入就是在被测代码中自动插入用于覆盖率统计的探针(Probe)代码,并保证插入的探针代码不会给原代码带来任何影响。

对于 Java 代码来讲,根据注入目标的不同,可以分为源代码(Source Code)注入和字节码(Byte Code)注入两大类。基于 JVM 本身特性以及执行效率的原因,目前主流的工具基本都是使用字节码注入,注入的具体实现采用 ASM 技术。

ASM 是一个 Java 字节码操纵框架,能被用来动态生成类或者增强既有类的功能,可以直接产生 class 文件,也可以在类被加载入 JVM 之前动态改变类行为。根据注入发生的时间点,字节码注入又可以分为两大模式:On-The-Fly 注入模式和 Offline 注入模式。

  • 第一,On-The-Fly 注入模式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 额外开启代理,缺点是无法实时获取代码覆盖率信息,只能在系统停机时下获取。Offline 模式根据是生成新的 class 文件还是直接修改原 class 文件,又可以分为 Replace 和 Inject 两种不同模式。和 On-The-Fly 注入模式不同,Replace 和 Inject 的实现是,在测试运行前就已经通过 ASM 将探针插入了 class 文件,而在测试的运行过程中不需要任何额外的处理。Cobertura 就是使用 Offline 模式的典型代表。

7、总结

  • 测试覆盖率通常被用来衡量测试的充分性和完整性,包括面向项目的需求覆盖率和更偏向技术的代码覆盖率。而需求覆盖率的统计方式不再适用于现在的敏捷开发模式,所以现在谈到测试覆盖率,大多是指代码覆盖率。
  • 但是,高的代码覆盖率不一定能保证软件的质量,因为代码覆盖率是基于现有代码,无法发现那些“未考虑某些输入”以及“未处理某些情况”形成的缺陷。
  • 另外,对于代码覆盖率的统计工具,我希望你不仅仅是会用的层次,而是能够理解它们的原理,知其然知其所以然,才能更好地利用这些工具完成你的测试工作。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值