Java单元测试实践笔记

前言: 本文适用于已经写了一段时间单元测试,并对单元测试的合理性和有效性有进一步追求的朋友。

在编程中,测试是必不可少的一环。其中单元测试作为最初的测试,也是最小粒度的测试,是十分关键的。在这里记录一下我对Java单元测试的一些理解和实践心得。

1. 选用合理的测试框架

JUnit测试中,分为很多场景:

  • 无第三方接口调用的单元测试
  • 方法中调用了第三方接口的单元测试
  • 方法中使用了静态方法
    其中第一种情况最好解决,只需要引入JUnit依赖就好了。第二种场景,常见的处理方法是打桩和mock,这两种方法的实质都是模拟/生成一个被调用的对象,都可以对需要调用第三方接口的方法进行测试。本人更倾向于使用mockito进行mock测试,一系列工具的注解和方法会使单元测试写起来更整洁可读性更好。对于调用了静态方法的方法单元测试,可以用PowerMock进行测试。

2. 对于不同场景的输入用例枚举

代码中,我们一般要考虑不同场景的输入情况来设计我们的测试用例:

  • 调用的接口有预期内的异常情况
  • 调用的接口有预期外的异常情况(不在约定之内),也就是兜底情况
  • 输入函数的参数有误的情况
  • 输入函数的参数越界情况(比如输入列表对象约定最大是100,但是实际调用时输入了101个)
  • 测试函数中调用第三方服务时的参数正确性
    上述的场景除了最后一种都是很好理解的,其中参数越界的情况尤为注意(我在开发中经常遇到),往往我们在和前端或者是后台其他服务调用约定协议时经常忽略这一点,考虑参数越界的情况的处理是很有必要的。如果接口的定义方和使用方都不对这一点进行考虑,那么当真正遇到越界情况,可能两方的(自己认为的应该这么做)默认处理不一致,最后导致bug的产生。

3. 对于函数中被调的次数/参数正确性的测试

一般写单元测试都会忽略这一部分的重要性,写调用次数,被调第三方函数的入参确认可以有效避免很多的bug。比如在mock时候使用eq参数对函数的输入参数进行确认,在不需要执行的地方Assert.fail来保证没有执行到该步骤,在不能抛异常的测试用例中。对于调用次数,参数情况的确认建议用verify。在我review过的代码中,有些人对返回类型为void的函数不去测试,这其实是一个隐患。返回为void类型的正适合对函数中调用的第三方接口进行verify以及用eq对参数进行校验。举个例子,在一个服务中你处理完了一批数据,最后调用一个返回为void的save方法为结束,最后整个方法也返回void。那么我们是不是可以在save方法中进行verify确认函数的入参(你处理完的数据)为你期望的数据,对于你不期望的入参,调用次数verify可以设置为0,这样就有效对返回为void的函数进行了有效测试。

4. 单元测试覆盖率

比如我们团队对于单元测试覆盖率的要求是百分之90,这个覆盖率只是一个观测指标,覆盖率越高虽然不一定代表测试做好了,但是覆盖率低肯定是测试不到位,有问题的。比如你的单元测试中虽然覆盖到了某个分支路径,但是没有做好相应的verify和assert,那么这个测试就是失败的,这样的单元测试并不能保证单元的正确性。比如你修复了一个bug,发现测试代码都不用改就通过了,并且覆盖了,那么就说明之前你的测试做的不够充分,尽管覆盖了达到了,也需要补充测试用例以保证单元测试的有效性。

5. 单元测试的用例的规范

代码是给人看的,测试代码也同理,有效的测试代码注释能让读或者维护代码的人更容易理解。建议在复杂的测试场景的测试用例前添加一定的注释说明,测试函数我们一般形如:testHelloWorld_httpReturnError的命名,然后函数的上面添加java doc类型的注释说明,比如是测http状态码返回400的场景等。还有就是测试代码行数不宜过长,如果很长,建议抽取相应的私有函数,这样使得测试逻辑更加清晰。

6.测试用例

测试用例testCase的输入我们一般会抽取成一个setUp函数,进行成员的mock返回设置
如果是所有测试函数启动的前置全局设置,可以使用@Before或者@BeforeEach,一般习惯性每一个函数里面去调用setUp使得每个测试用例隔离开。如果测试用例的数据字符串过长,推荐使用文件进行读取。

7.TDD测试驱动开发

因为我的组长是测试领域的专家,在公司内部开过很多次TDD的课程,在他的耳濡目染下我也对TDD有一定的感受和理解。TDD的中文名是测试驱动开发,说通俗一点就是先有测试用例,再进行相应的开发。每次编写完测试用例,定好确定的输入输出,再去写开发代码,然后在测试用例的不断完善下逐渐完善接口。这种思想脱离了以往我们先开发再补测试的思想,先开发再测试有个很大的问题就是开发过程中的分支情况你已经想过一遍了,再编写测试用例的时候就会被限制住而有些场景没有考虑到。如果我们先写测试用例,再去迭代开发的话,更贴近于逐步描绘一个需求,不断完善需求细节的场景,这样在测试中开发,不容易漏掉细节。这种开发方式有好又怀,好处是代码的bug率大大下降,思路的转变使得在开发复杂需求的时候不容易出错。不好的地方就是因为需要时间去制定完善的测试用例,需要大量的时间来写测试和接口,还有就是需要习惯这种开发模式需要一定的时间。这种方式在我们部门业务闲的时候试行过一阵子,后面业务紧了一天要几个接口,这种模式也就没有再用了,但是不影响TDD是一个值得学习的开发模式。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%