怎么写单元测试?

单元测试非常重要,一定要认真写!

本文中我使用junit进行单元测试。

 

一、单元测试的重要性

之前我写代码,只是调通接口,能在各种情况下得出相应的结果,我就很满足了,不再进行更深一步的测试。后来才发现,这根本不是测试!只是调通接口罢了。

试想:如果没有单元测试,我们如何保证代码质量呢?

简单的调通接口,测试用例基本上不会很多,没有涵盖各种情况。如果刚好某种错误没测试到,后面的开发可能会在错误的代码上累积起来。一旦出bug,需要在一堆代码中寻找bug,不好发现和重现。如果问题出在结构上,甚至难以修改(在软件工程中,越早出现的问题,后面解决起来成本越高)。以上的两种情况,都很可能造成大量返工,浪费效率。

假如证明你的代码是正确无误的,要怎么证明呢?难道每次都要手工输入参数,调试接口吗?如果不记录测试用例,难道每次都都要重新设计吗?这样的测试是不可重现的,需要大量的重复劳动,同时还没有办法保证测试质量。

所以,一定要进行单元测试。

我知道单元测试很复杂,设计测试用例很花时间。但是单元测试的覆盖面最广(需要设计各种测试用例,涵盖各种情况),结果最可靠(测试过程有代码,可以重现各种情况),最能证明你的代码没问题(每次发布项目都可以先行测试,保证代码质量)。开发人员要展示自己的工作成果,必须通过单元测试的代码去自证:我写的东西一点问题都没有,工资要按时给我哦。

二、如何进行单元测试?

举个例子:

我个人认为需要这三个步骤:

1.设计测试用例

要以代码的形式记录下来,便于以后验证。

2.实际运行功能

就是跑你的代码的逻辑部分,传入测试用例后,需要输出相应的输出结果。

3.进行结果校验

需要使用junit提供的Assert.assertEquals();方法,功能是校验结果是否和预期一致。如果一致,才能通过测试。否则将爆出测试不通过的错误。

三、如何同时测试多个测试用例?

可能涉及到一些写代码的技巧,大概说一下我的心得。

1.可以像这样写两个不同的测试用例:

可以发现,这两个测试用例除了“海之光”和“客户”这两个字段外,其他完全就是一样的。所以可以进行重用。

2.可以重用:

第二个测试用例,可以在第一个测试用例上稍作修改之后得来,因此少写了很多代码。

软件测试经常要测两个极端值,然后边界值也要测(比如min为0,max为1,因为有五个测试区间,所以就要写五个测试用例)。这就意味着很多时候只是一个值要变多次,其他值不用变。

3.这时候也可以使用一个HashMap:

这样也可以。而且看上去可能更加简洁一些。

4.这里要注意一个问题

(1)如果写成这样:

就会遇过这样一个报错:

assertEquals方法在类型转换上遇到了问题。为什么呢?

原因是这样的:如果这里不强制进行类型转换,那么assertEquals方法中的两个参数就是一个int基本类型的,一个Interger包装类型的。

看一下assertEquals的源码,发现这是一个重载方法:

重载方法有很多,但是找不到比较Interger和int的方法。所以这时候编译器调用assertEquals(Object expected, Object actual)这个重载方法了。

既然要比较,那么assertEquals当然要想办法把两个不同类型的参数转成同类型的参数,不然怎么比较呢?所以这里就出现了歧义,可以把int转成Interger,也可以把Inter转成int,这时候编译器就不知所措了。在这种情况下,就需要我们人工做出类型的选择,把两种类型转换为同一种类型,才能进行比较。

所以在这里,既然

能正常运行,那么就意味着这样也行:

(2)如果写成这样,也会报同样的错

这样写的错误原因和上面那种是不同的。在这里,编译器可以找到如下两种构造方法:

assertEquals(Object,Object)

assertEquals(Long, Long)

问题出在编译器要选择哪种方法呢?首先在assertEquals(10L, (Long)10)中,一个参数是基本类型long,另一个是基本类型是包装类Long,找不到assertEquals(long, Long)这样的构造方法。

其次,因为long可以自动转换(autobox)成Long,所以说可以理解成你想调用assertEquals(Long,Long);但是10L和(Long)10都是Object类型的,所以说你想调用assertEquals(Object,Object)也合情合理。

所以,只要像这样出现歧义,编译器就会报错。

四、总结

总而言之,单元测试非常重要,一定要用心设计,用心去写哦。(但还是挺麻烦的)

发表评论

电子邮件地址不会被公开。 必填项已用*标注