首页 > 代码库 > 原!关于java 单元测试的一些总结

原!关于java 单元测试的一些总结

最近项目有在写java代码的单元测试,然后在思考一个问题,为什么要写单元测试??单元测试写了有什么用??百度了一圈,如下:

  • 软件质量最简单、最有效的保证;
  • 是目标代码最清晰、最有效的文档;
  • 可以优化目标代码的设计;
  • 是代码重构的保障;
  • 是回归测试和持续集成的基石。

由于开发经验有限,可能说的不太对,但是是我目前的个人的观点,写单元测试,有时候确实可以发现bug,但是发现bug次数很少,而且目前都是项目开发完了,要上线了,公司有80%的覆盖率要求,所以都是后期上线之前补。目前而言,并没有在很认真地写UT,只是想着完成上线要求。这个东西吧,也是看成本要求,如果一个新项目要紧急上线,走紧急发布特殊流程,单元测试后期时间充裕了再补上也行。所以,在时间允许情况下,我觉得还是要写UT,做了有时候确实能发现一些问题,尤其对于一个大的项目来说,一个 bug 被隐藏的时间越长,修复这个 bug 的代价就越大。在《快速软件开发》一书中已引用了大量的研究数据指出:最后才修改一个 bug 的代价是在 bug 产生时修改它的代价的10倍。此外,还能学到一些单元测试的知识,也算是一种技能上的进步吧。

 

一、目前应用比较普遍的java单元测试工具 junit4+Mock(Mockito /jmock / powermock)Stub(用得较少,一般不推荐)由于junit3目前用得不多,基本升级到junit4了,所以就直接简单说下junit4。

 

问题一:为什么需要mock或stub?它与junit什么关系?

在做单元测试的时候,我们会发现我们要测试的方法会引用很多外部依赖的对象,比如:(发送邮件,网络通讯,记录Log, 文件系统 之类的)。 而我们没法控制这些外部依赖的对象。  为了解决这个问题,我们需要用到Stub和Mock来模拟这些外部依赖的对象,从而控制它们。

JUnit是单元测试框架,可以轻松的完成关联依赖关系少或者比较简单的类的单元测试,但是对于关联到其它比较复杂的类或对运行环境有要求的类的单元测试,模拟环境或者配置环境会非常耗时,实施单元测试比较困难。而这些“mock框架”Mockito 、jmock 、 powermock、EasyMock),可以通过mock框架模拟一个对象的行为,从而隔离开我们不关心的其他对象,使得测试变得简单。(例如service调用dao,即service依赖dao,我们可以通过mock dao来模拟真实的dao调用,从而能达到测试service的目的。

模拟对象(Mock Object)可以取代真实对象的位置,用于测试一些与真实对象进行交互或依赖于真实对象的功能,模拟对象的背后目的就是创建一个轻量级的、可控制的对象来代替测试中需要的真实对象,模拟真实对象的行为和功能。

 

问题二:mock与stub什么区别?

Mock和Stub是两种测试代码功能的方法。Mock测重于对功能的模拟,Stub测重于对功能的测试重现。比如对于List接口,Mock会直接对List进行模拟,而Stub会新建一个实现了List的TestList,在其中编写测试的代码。
强烈建议优先选择Mock方式,因为Mock方式下,模拟代码与测试代码放在一起,易读性好,而且扩展性、灵活性都比Stub好。

其中EasyMock和Mockito对于Java接口使用接口代理的方式来模拟,对于Java类使用继承的方式来模拟(也即会创建一个新的Class类)。Mockito支持spy方式,可以对实例进行模拟。但它们都不能对静态方法和final类进行模拟,powermock通过修改字节码来支持了此功能。

有篇文章介绍:http://blog.csdn.net/devhubs/article/details/8018084

 

二、junit4相关介绍

这里有篇文章介绍了junit4的一些,包括怎么引入,使用,蛮详细。---》 http://blog.csdn.net/happylee6688/article/details/38069761

这边就记录一些常用注解,当做学习方便。

常用注解

@Before:初始化方法,在任何一个测试方法执行之前,必须执行的代码。对比 JUnit 3 ,和 setUp()方法具有相同的功能。在该注解的方法中,可以进行一些准备工作,比如初始化对象,打开网络连接等。

@After:释放资源,在任何一个测试方法执行之后,需要进行的收尾工作。对比 JUnit 3 ,和 tearDown()方法具有相同的功能。
 
@Test:测试方法,表明这是一个测试方法。在 JUnit 中将会自动被执行。对与方法的声明也有如下要求:名字可以随便取,没有任何限制,但是返回值必须为 void ,而且不能有任何参数。如果违反这些规定,会在运行时抛出一个异常。不过,为了培养一个好的编程习惯,我们一般在测试的方法名上加 test ,比如:testAdd()。

同时,该 Annotation(@Test) 还可以测试期望异常和超时时间,如 @Test(timeout=100),我们给测试函数设定一个执行时间,超过这个时间(100毫秒),他们就会被系统强行终止,并且系统还会向你汇报该函数结束的原因是因为超时,这样你就可以发现这些 bug 了。而且,它还可以测试期望的异常,例如,我们刚刚的那个空指针异常就可以这样:@Test(expected=NullPointerException.class)。

 @Ignore:忽略的测试方法,标注的含义就是“某些方法尚未完成,咱不参与此次测试”;这样的话测试结果就会提示你有几个测试被忽略,而不是失败。一旦你完成了相应的函数,只需要把 @Ignore 注解删除即可,就可以进行正常测试了。当然,这个 @Ignore 注解对于像我这样有“强迫症”的人还是大有意义的。每当看到红色条(测试失败)的时候就会全身不舒服,感觉无法忍受(除非要测试的目的就是让它失败)。当然,对代码也是一样,无法忍受那些杂乱不堪的代码。

@BeforeClass:针对所有测试,也就是整个测试类中,在所有测试方法执行前,都会先执行由它注解的方法,而且只执行一次。当然,需要注意的是,修饰符必须是 public static void xxxx ;此 Annotation 是 JUnit 4 新增的功能。

@AfterClass:针对所有测试,也就是整个测试类中,在所有测试方法都执行完之后,才会执行由它注解的方法,而且只执行一次。当然,需要注意的是,修饰符也必须是 public static void xxxx ;此 Annotation 也是 JUnit 4 新增的功能,与 @BeforeClass 是一对。

 

执行顺序

所以,在 JUnit 4 中,单元测试用例的执行顺序为:
技术分享
 
 

三、Mock的几种比较(Mockito 、jmock 、 powermock)

 文章一:http://blog.csdn.net/luvinahlc/article/details/10442743

原!关于java 单元测试的一些总结