单元测试的原则:AIR、BCDE、FIRST、3R、3A、SOCKS、Right-BICEP 右臂二头肌
今天随便翻了一下《阿里巴巴 JAVA 开发规范》,看到单元测试中提到了“AIR”原则,而我之前了解的是“FIRST”原则,不管什么原则都念叨一下总是有所裨益的,我都在这里归集一下吧。
AIR 原则
好的单元测试必须遵守 AIR 原则。
单元测试在线上运行时,感觉像空气 (AIR) 一样并不存在,但在测试质量的保障上,
却是非常关键的。好的单元测试宏观上来说,具有自动化、独立性、可重复执行的特点。
- A: Automatic (自动化)
单元测试应该是全自动执行的,并且非交互式的。测试用例通常是被定期执行的,执行过程必须完全自动化才有意义。输出结果需要人工检查的测试不是一个好的单元测试。单元测试中不准使用 System.out 来进行人肉验证,必须使用 assert 来验证。
- I: Independent (独立性)
保持单元测试的独立性。为了保证单元测试稳定可靠且便于维护,单元测试用例之间决不能互相调用,也不能依赖执行的先后次序。 反例:method2 需要依赖 method1 的执行,将执行结果作为 method2 的输入。
- R: Repeatable (可重复)
单元测试是可以重复执行的,不能受到外界环境的影响。 说明:单元测试通常会被放到持续集成中,每次有代码 check in 时单元测试都会被执行。如果单测对外部环境(网络、服务、中间件等)有依赖,容易导致持续集成机制的不可用。 正例:为了不受外界环境影响,要求设计代码时就把 SUT 的依赖改成注入,在测试时用 spring 这样的 DI 框架注入一个本地(内存)实现或者 Mock 实现。
FIRST 原则
- Fast 快速
测试如果跑得不够快,就不会让人想常常跑,不常跑的测试最后也就失去的它意义了。
所以实务上会使用 mock 工具,mock 其他依赖的物件或环境,来加速测试的执行。
- Independent 独立
测试要相互独立,一个测试不会依赖其他测试,如果互相依赖的话,一个测试的失败会影响其他测试也跟着失败,那么在找问题点的时候将会变得更困难。
- Repeatable 可重复
测试应该要可以在任何环境中重复执行。减少因环境因素而产生测试失败的问题。
- Self-Validating 自我验证
测试程式应该要输出布林值。不管是测试成功或失败。
简单来说就是可以在测试报告很清楚的看到红灯(测试失败) 或绿灯(测试成功)。
- Timely 及时
撰写测试要及时,最好是在写产品程式前先写(TDD 的概念)。
BCDE 原则
编写单元测试代码遵守 BCDE 原则,以保证被测试模块的交付质量。
- Border,边界值测试,包括循环、 特殊取,边界值测试包括循环、 特殊取特殊时间点、数据顺序等。
- Correct,正确的输入,并得到预期结果。 ,正确的输入并得到预期结果。
- Design,与设计文档相结合,来编写单元测试。 ,与设计文档相结合来编写单元测试。
- Error,强制错误信息输入(如:非法数据、异常流程业务允许等),并得 ,强制错误信息输入(如:非法数据、异常流程业务允许等),并得到预期结果。
测试边界原则 - CORRECT
- Conformance - 一致性 (值是否符合预期的格式?)
- Ordering - 有序性 (一组值的顺序是否符合预期?)
- Range - 区间性 (值是否在一个合理的最大值和最小值的范围内?)
- Reference - 引用性 / 耦合性 (代码是否引用了一些不受其直接控制的外部因素,由这些外部因素所引入的前置条件或后置条件所造成的影响是否符合预期?)
- Existence - 存在性 (值是否存在(例如:非 null,非零,存在于某个集合中等)? )
- Cardinality - 基数性 : 计数性 (是否恰好有足够的值?(重点关注 0-1-n 原则,问题往往发生在这三个边界上。))
- Time - 时间性(绝对时间及相对时间)- (所有事情是否按顺序发生?是否在正确的时间?是否及时?)
3R 原则
为了提高开发人员的代码质量,编写高质量的单元测试,要遵守 3R(Responsible, Reliable, Repeative)原则,具体含义如下:
- Responsible: 谁开发谁负责测试,在哪里开发就在哪里测试。
开发在完成一个方法,或者一个类之后,就要及时得进行单元测试;不能在对应方法或类的调用处进行测试,比如两个模块 A、B,A 是基础模块,为模块 B 提供服务,那么所有 A 模块的单元测试 case 都应该在 A 模块的内部进行测试。
- Reliable: 测试 case 要可靠,并且是值得信赖的,对于底层的任何改动都要能够及时感知。
为了使得测试用例尽量可靠,就要减少 mock 的使用(对于第三方的调用可以使用 mock),对每层代码的测试都要完全依赖于下层,不能 mock 下层逻辑。因此引入递进集成的概念,比如测试 DAO 时要连接真实的数据库,测试 Service 时要使用真实的 DAO、DB, 测试 Controller 层的代码,要使用真实的 Service、DAO、DB,以此类推。这样就可以最大限度的提高 case 的可靠性。
- Repeative: 所有单元测试用例都要能够重复运行。能够重复运行就能够进行回归测试、覆盖率统计等等。
必须要做到 case 间完全解耦,没有任何的依赖,这包括和数据库的依赖以及第三方的依赖。case 解耦可以通过准备测试数据、mock 第三方调用来解决。
推荐的测试结构:AAA (Arrange - Act - Assert),或 GWT (Given - When - Then)
3A 原则原本是单元测试用例编写时应该遵循的基本原则
- Arrange: 初始化测试对象或者准备测试数据
arrange 初始化测试数据,就是造数据,这里的数据有我们输入的数据,也有目标接口所涉及的资源,比如 hr 系统中的用户信息,我们必须先有几条人员的详细信息才能去测获取人员信息的接口(当然只是正常的流程,我们有时候还需要清掉数据以便测试资源为空的情况);
- Act : 通过不同的参数来调用接口,并拿到返回
act 调用接口,传入输入数据;
- Assert: 必须做断言,否则用例就没有任何意义了
assert 断言, 对返回的资源信息进行断言,比如获取用户信息的接口返回了用户信息之后,我们要判断返回的用户是不是我们想要的那个用户,我们获取的是李雷的信息,接口如果返回韩梅梅,那么接口的逻辑就是不对的;
提升可测试性的袜子模型- SOCKS ,或者 SOCK
- Simple:一个一百行的程式和一个一千行的程式哪一个比较好测?应该是前者比较容易测试。同理,一个模组化做得很好的程式,和一坨义大利面相比,前者比较容易理解,当然也比较容易测试。
- Observable:俗话说「可观察才可测量」,如果程式的内在结构或是外在行为不容易观测,那么这个程式就不容易测试。
- Control:能够控制软体的行为,也会让测试变得容易许多。
- Knowledge:对于待测程式的相关知识越充足,也会提升待测程式的可测试性。例如,假设乡民们要测试 isPrime(int value) 这个判断一个整数是否为质数的函数。如果乡民们连质数的定义都不了解,当然也无法帮 isPrime() 写测试案例(无法定义出 expected result 啊)。所以,提供越清楚、正确的说明文件或是任何有关待测程式的知识,便可提高程式的可测试性。
- Stability: 功能稳定的系统才方便单元测试。
理想的单元测试金字塔与现实中的冰激凌蛋筒
Testing pyramid
Testing ice cream cone
Testing hourglass
测试什么? Right-BICEP(右臂二头肌) 法则
一个有关「单元测试」的标准。其中 Right 指程序的正确性,BICEP 每个字母代指一个测试的方面:
- Right: 传入程序单元期待的参数、数据,是否能够得到正确的结果。
- Boundary Conditions: 程序单元是否能够正确处理所有的边界条件。
- Inverse relationships: 反向测试。比如你写了一个求平方根的函数,那么它的反向就应该是求平方。因此,你需要验证 x=(x√)2x=(x)2。
- Cross-check: 交叉测试。比如你写了一个求平方根的函数,那么你可以用你的函数产生的结果与系统库里对应函数产生的结果做对比。它们应该得到相同的结果。
- Error-condition: 异常情况。你应该验证在发生异常情况时,你的程序能够正确处理。比如:硬盘满了、当前目录无写入权限、网络通讯中断了等等。
- Performance: 性能。你应该检查,在程序有大量输入的情况下,程序是否正常运作。
大话西游之单元测试
我知道这个项目 bug 很多,无法按时完成,即使老板把我炒了也是应该的。曾经有一个做单元测试的机会放在我面前,我没有珍惜,等到后来项目雪崩了才后悔。如果上天能给我再来一次机会的话,我会对老板说:我要做单元测试!如果一定要在单元测试上加个日期,我希望是一直。
按照 Mike Cohn 提出的“测试金字塔”概念,测试分为 4 个层次
本文讨论了 13 种软件测试的错误做法,比如:
- 单元测试与集成测试的比例不正确;
- 太相信 TDD(即写代码之前就写测试);
- 写了大量很慢的测试;
- 手动运行测试;
- 没有读懂文档,就写测试 ;
- 没有把生产环境的错误写成测试等等。
《为什么大多数单元测试是浪费》(Why Most Unit Testing is Waste)这篇文章是完全不同的:它从信息论和开发过程本质的层次上揭示了单元测试所固有的根本性缺陷,同时也引用了大量学术论文与研究成果,证明单元测试在实践上的成本效益是非常有限的。作者的结论,并不是要完全取消单元测试,而是应该严格限制单元测试的使用。重要的论点包括:
- 单元测试应该仅用于关键性的独立算法;
- 不要用测试驱动设计,而是需求驱动设计;
- 对于业务组件的正确性验证应该使用集成测试或系统测试;
- 综合使用多种质量保证手段,包括调试和代码审查;
- 扔掉一年以内从未失败过的测试;
- 将测试转换为断言;
SQLite 测试代码的行数,是软件本身的 662 倍。
As of version 3.29.0 (2019-07-10), the SQLite library consists of approximately 138.9 KSLOC of C code. (KSLOC means thousands of "Source Lines Of Code" or, in other words, lines of code excluding blank lines and comments.) By comparison, the project has 662 times as much test code and test scripts - 91946.2 KSLOC.
-- 《如何测试 SQLite》
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: 玩转一下 SonarQube
下一篇: 不要相信一个熬夜的人说的每一句话
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论