作者 | S.L
来源 | http://r6d.cn/aaTem
广义的测试包括 UT、IT、压力测试、硬件测试等等,这里重点讨论 Unit Test 即单元测试。
单元测试(又称为模块测试, Unit Testing)是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。
简而言之就是覆盖你的代码的一些测试用例,不依赖于任何第三方的服务依赖,如 HTTP 接口、数据库连接等,只测试功能不依赖于环境,在任何时候人和机器上都可以 Pass。
让你的代码质量更可靠&让你对代码结构更加敏感&迫使你写更优质的代码&…
!${为什么要写 UT}
更多的代码量,加上单测代码并不像业务代码那样直观,还有对单测代码可读性不重视的坏习惯,导致最终呈现出来的单测代码难以阅读,要维护更是难上加难。
至少要满足:
Java 开发一般都是用 JUnit 或 TestNG,我们大多人还是使用 JUnit4。本文不讨论语法,只介绍一般性的使用规范。
可以参考 7 Popular Unit Test Naming ( https://dzone.com/articles/7-popular-unit-test-naming )
任何一个 UT 中需要至少包含一个 assert,用 System.out.println()来验证结果不符合 UT 的规范,一般都是验证方法的返回结果,如 assertEquals(200, statusCode)而不是 System.out.println(200==statusCode)。
Assertion 只能保证走过的分支的结果是否正确,无法保证一定是走过了某些分支。
不用 Mock 我们自己也能实现测试(如匿名类),只不过对代码的要求非常高
一些常用的 mock 库包括 Mockito、JMockIt、EasyMock、PowerMock…没有优劣没有好坏,只有合适与否。
比如我个人比较喜欢 Mockito:
如果把一种依赖写死在方法里肯定不利于测试,如果该依赖是一种强引用第三方服务的 sdk 你就痛苦了,如配置类初始化时需要连接 zk 且无法注入
包括类的抽象、方法的提取,代码越精简,测试越方便、越快速、越容易暴露问题
面向扩展开发,面向修改闭合,不对老代码入侵,避免 UT 重复修改
最好的 static 方法是完全不依赖任何第三方服务自己可以实现业务逻辑的代码,如果依赖第三方,使用 reference 传入而不是写死在 class 或 method 里
单元测试测试的对象是类,测试类的功能在各种情况下是否符合预期,而不是测试实现。所以我们只需要测试能够跟其他类交互的 public 方法就可以了。这样的一个好处就是,如果哪天需要重构代码的实现,或者换一个算法实现某些方法,但功能不变的情况下,UT 是可以复用的。如果针对实现来测试,如果哪天要重构代码实现,那 UT 就会 fail 掉。
一个仁者见仁智者见智的问题,不做深入讨论了。
个人建议工具方法(保证正确性以及边界条件不出错)、核心流程(复杂的条件判断尤其需要 UT 保证)需要重点覆盖,底层接口如 DAO、简单的 Service 封装可以不用写。
推荐一个 JUnitGeneratorV2.0,可以通过 Command+N 来生成 Test 类或者直接在类名上使用 alt+enter 来生成。
没有测不了的代码,只有测试不够方便的代码,大多源于设计不够合理。
写单元测试的难易程度跟代码的质量关系最大,并且是决定性的。项目里无论用了哪个测试框架都不能解决代码本身难以测试的问题,所以如果你遇到的是“我的代码里依赖的东西太多了所以写不出来单测”这样的问题的话,需要去看的是如何设计和重构代码,而不是这篇文章。
点击下方卡片关注我,订阅更多精彩内容
往期推荐
“智能”坐垫记录离座时间,是高科技福利还是又一个员工压榨机器?
还在封装各种 Util 工具类?这个神级框架帮你解决所有问题!