写点什么

原创 | 使用 JUnit、AssertJ 和 Mockito 编写单元测试和实践 TDD (六)测试哪些内容:Right-BICEP

发布于: 2020 年 05 月 14 日
原创 | 使用JUnit、AssertJ和Mockito编写单元测试和实践TDD (六)测试哪些内容:Right-BICEP





上一章通过实例讲了“第一个单元测试”到底应该怎么做,这一章我们讲讲“对一个工作单元需要测试它哪些方面的内容”?



  • Right——结果是否正确?

  • B——是否所有的边界条件都是正确的?

  • I——能查一下反向关联吗?

  • C——能用其他手段交叉检查一下结果吗?

  • E——你是否可以强制错误条件发生?

  • P——是否满足性能要求?



1. 结果是否正确

例如对于上一节的取款案例:原有余额10000元,取款2000后,余额应该剩下8000元。我们就要测试这个结果:

assertThat(account.getBalance()).isEqualTo(8000);

断言失败就表明实现代码有错,需要修改后重新测试。



2. 边界条件

代码中的bug往往都出现在“边界条件”附近,也就是说,在那些条件下,代码的行为可能不同于平常的、每天都能运行到的程序路径。例如:

  • 我们期待接受一个代表文件路径的字符串,但客户代码可能传入一个包含回车或冒号的字符串。

  • 我们期待接受一个代表email的字符串,但客户代码可能传入一个没有包含“@”的字符串。

  • 我们期待接受一个正数,但客户代码可能传入0或负数。

  • 我们期待传入一个对象,但客户代码可能传入null。

  • 我们要从传入的集合中选择第一个元素,但客户代码传入了一个空集合。

如果你是一个桥梁工程师,大桥建好之后,你不能只是在风和日丽的日子里,让一辆车缓缓驶过桥面,就宣布大桥经过了充分测试。作为工作单元的实现者,我们的代码交付后,代码的用户可能会用各种奇葩的方式调用我们的代码,我们必须预先针对这种种可能情况预先设计应对策略,并通过单元测试来确保工作单元在各种边界条件下都会按照我们的预设策略那样执行。



一个想到可能的边界条件的简单办法就是记住助记词CORRECT。下一节我们将详细论述CORRECT边界条件。



3. 检查反向关联

例如我们要检查求平方根的函数squareRoot()的正确性,就可以通过检查平方根的平方是否等于当初的参数的当时来检查代码实现的正确性:

@Test
void testSquareRootUsingInverse() {
double a = 8;
double result = squareRoot(a);
assertThat(result * result).isCloseTo(a, Percentage.withPercentage(0.00001));
}

上面的测试代码用于测试squareRoot(double a)函数的正确性。a的平方根的平方应该等于a。

说明:在计算机中浮点数无法精确比较其相等性,因此,两个数只要足够接近,就可以认为相等。在上面的测试例子中,我们把足够接近定义为相差不超过0.00001%。



4. 使用其他手段来实现交叉检查

通常而言,实现一个工作单元有一种以上的算法。我们选用其中一种最好的来作为我们的代码实现,但可以使用其余的算法来作为单元测试。当两者的计算结果都相同时,我们就可以认为我们的代码实现是正确的。当然前提是两种算法不会都是错误的,但恰好都产生相同的结果。

例如JDK标准库中已经有Math.sqrt()这样的一个平方根函数。当我们的平方根函数和标准库中的平方根函数得出的结果相同时,就可以认为我们的的平方根函数是正确的。

@Test
void testSquareRootUsingStd() {
double a = 8;
assertThat(squareRoot(a)).isCloseTo(Math.sqrt(a), Percentage.withPercentage(0.00001));
}



5. 强制产生错误条件

在真实世界中,错误总会发生:磁盘会满,WIFI会断开,程序会崩溃。你应该能够通过主动强制引发这些错误,来测试你的代码在这样极端状态下是如何应对这些真实世界中的问题的。

这就是使用Mockito这样的测试替身库的作用之一——它们能够模拟各种各样的异常条件,而不需要无限期地消极等待真实世界中异常状态的出现。



6. 性能特性

一般的性能测试是黑盒测试,采用JMeter这样的专门的性能测试工具进行测试。但是对于我们所写的代码,如果里面包含了复杂的循环,或者处理了大量的数据,那就应该在单元测试层面测试一下性能。

通常会有性能问题的测试都会耗时较长。我们不希望被这些测试拖慢进度。这时可以采用JUnit的测试分组策略,将这些耗时的测试分到另外的组,只在某些阶段执行一下测试。而其他的单元测试会经常运行。



这一章我们讲到这里,下一章将讲讲“CORRECT边界条件”!





发布于: 2020 年 05 月 14 日阅读数: 92
用户头像

高级架构师,技术顾问,交流公号:编程道与术 2020.04.28 加入

杨宇于2020年创立编程道与术,致力于研究领域分析与建模、测试驱动开发、架构设计、自动化构建和持续集成、敏捷开发方法论、微服务、云计算等顶尖技术领域。 了解更多公众号:编程道与术

评论

发布
暂无评论
原创 | 使用JUnit、AssertJ和Mockito编写单元测试和实践TDD (六)测试哪些内容:Right-BICEP