目录html
本身认为,单元测试最重要的做用有以下两点java
①开发人员实现某个功能或者修补了某个bug,若是有相应的单元测试支持的话,开发人员能够立刻经过运行单元测试来验证以前完成的代码是否正确,而不须要反复经过发布war包、启动jboss、经过浏览器输入数据等繁琐的步骤来验证所完成的功能git
②保证你最后的代码修改不会破坏以前代码的功能。项目越作越大,代码愈来愈多,特别涉及到一些公用接口之类的代码或是底层的基础库,谁也不敢保证此次修改的代码不会破坏以前的功能,因此与此相关的需求会被搁置或推迟,因为不敢改进代码,代码也变得愈来愈难以维护,质量也愈来愈差。而单元测试就是解决这种问题的很好方法(不敢说最好的)。因为代码的历史功能都有相应的单元测试保证,修改了某些代码之后,经过运行相关的单元测试就能够验证出新调整的功能是否有影响到以前的功能。固然要实现到这种程度须要很大的付出,不但要可以达到比较高的测试覆盖率,并且单元测试代码的编写质量也要有保证程序员
JUnit是一个Java语言的单元测试框架。它由Kent Beck和Erich Gamma创建,逐渐成为源于Kent Beck的sUnit的xUnit家族中最为成功的一个JUnit有它本身的JUnit扩展生态圈。多数Java的开发环境都已经集成了JUnit做为单元测试的工具。github
注意:Junit 测试也是程序员测试,即所谓的白盒测试,它须要程序员知道被测试的代码如何完成功能,以及完成什么样的功能spring
咱们知道 Junit 是一个单元测试框架,那么使用 Junit 能让咱们快速的完成单元测试。数据库
一般咱们写完代码想要测试这段代码的正确性,那么必须新建一个类,而后建立一个 main() 方法,而后编写测试代码。若是须要测试的代码不少呢?那么要么就会建不少main() 方法来测试,要么将其所有写在一个 main() 方法里面。这也会大大的增长测试的复杂度,下降程序员的测试积极性。而 Junit 能很好的解决这个问题,简化单元测试,写一点测一点,在编写之后的代码中若是发现问题能够较快的追踪到问题的缘由,减少回归错误的纠错难度。apache
一、在某些很是复杂的业务逻辑,会准备大量的数据。api
二、有的时候会依赖数据库,中间件、文件系统等外部环境,这个时候咱们不能控制这些外部依赖的对象。浏览器
试想一下,若是咱们依赖真实的数据库环境,那么每次的单元测试结果可能都是不同的
为了解决上述两个问题,咱们须要使用Mock技术
截取一段stackflow中的解释:
Mocking isprimarily used in unit testing. An object under test may have dependencies onother (complex) objects. To isolate the behaviour of the object you want totest you replace the other objects by mocks that simulate the behavior of thereal objects. This is useful if the real objects are impractical to incorporateinto the unit test.
MOCK主要被用于在单测中,某个对象在测试过程当中有可能依赖于其余的复杂对象,经过mocks去模拟真实的其余对象(模块)去代替你想要去测试的其余的对象(模块),若是其余对象(模块)是很难从单元测试中剥离开来的话,这是很是有用的
Mock有如下几个好处:
一、Mock能够用来解除测试对象对外部服务的依赖(好比数据库,第三方接口等),使得测试用例能够独立运行。无论是传统的单体应用,仍是如今流行的微服务,这点都特别重要,由于任何外部依赖的存在都会极大的限制测试用例的可迁移性和稳定性。可迁移性是指,若是要在一个新的测试环境中运行相同的测试用例,那么除了要保证测试对象自身可以正常运行,还要保证全部依赖的外部服务也可以被正常调用。稳定性是指,若是外部服务不可用,那么测试用例也可能会失败。经过Mock去除外部依赖以后,无论是测试用例的可迁移性仍是稳定性,都可以上一个台阶。
二、Mock的第二个好处是替换外部服务调用,提高测试用例的运行速度。任何外部服务调用至少是跨进程级别的消耗,甚至是跨系统、跨网络的消耗,而Mock能够把消耗下降到进程内。好比原来一次秒级的网络请求,经过Mock能够降至毫秒级,整整3个数量级的差异
三、Mock的第三个好处是提高测试效率。这里说的测试效率有两层含义。第一层含义是单位时间运行的测试用例数,这是运行速度提高带来的直接好处。而第二层含义是一个测试人员单位时间建立的测试用例数。如何理解这第二层含义呢?以单体应用为例,随着业务复杂度的上升,为了运行一个测试用例可能须要准备不少测试数据,与此同时还要尽可能保证多个测试用例之间的测试数据互不干扰。为了作到这一点,测试人员每每须要花费大量的时间来维护一套可运行的测试数据。有了Mock以后,因为去除了测试用例之间共享的数据库依赖,测试人员就能够针对每个或者每一组测试用例设计一套独立的测试数据,从而很容易的作到不一样测试用例之间的数据隔离性。而对于微服务,因为一个微服务可能级联依赖不少其余的微服务,运行一个测试用例甚至须要跨系统准备一套测试数据,若是没有Mock,基本上能够说是不可能的。所以,无论是单体应用仍是微服务,有了Mock以后,QE就能够省去大量的准备测试数据的时间,专一于测试用例自己,天然也就提高了单人的测试效率。
EasyMock 以及 Mockito 都由于能够极大地简化单元测试的书写过程而被许多人应用在本身的工做中,可是这两种 Mock 工具都不能够实现对静态函数、构造函数、私有函数、Final 函数以及系统函数的模拟,可是这些方法每每是咱们在大型系统中须要的功能。
PowerMock是一个扩展了其它如EasyMock等mock框架的、功能更增强大的框架。PowerMock使用一个自定义类加载器和字节码操做来模拟静态方法,构造函数,final类和方法,私有方法,去除静态初始化器等等。经过使用自定义的类加载器,简化采用的IDE或持续集成服务器不须要作任何改变。熟悉PowerMock支持的mock框架的开发人员会发现PowerMock很容易使用,由于对于静态方法和构造器来讲,整个的指望API是同样的。PowerMock旨在用少许的方法和注解扩展示有的API来实现额外的功能。目前PowerMock支持EasyMock和Mockito。
①Mockito底层使用了动态代理,用到了CGLIB。所以须要被mock的对象,Mockito都会生成一个子类继承该类,这也就是为何final类、private方法、static方法不能够被Mock的缘由
②powermock的底层原理
咱们首先看powermock的依赖
能够看出来,它有两个重要的依赖:javassist和objenesis。
javassist是一个修改java字节码的工具包,objenesis是一个绕过构造方法来实例化一个对象的工具包。由此看来,PowerMock的本质是经过修改字节码来实现对静态和final等方法的mock的
下面是PowerMock的简单实现原理:
这里吐血推荐一本电子书
网盘连接: https://pan.baidu.com/s/1ZndwumRgSTqmtn__RNVosA 密码:e8w4
必定要注意powermock的版本号,我在这里就踩了不少坑,血淋淋的教训啊,因为公司与springboot继承用的是最新的powermock,截止2019年4月12日,powermock-module-junit4的Maven地址仓库最新版本是2.0.0 ,也正是powermock使用的太新了,致使后面遇到的问题,百度google根本没法解决(由于他们都还停留在1.x版本中),最后也是经过github官方文档才最终解决的
<dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>2.0.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito</artifactId> <version>2.0.0</version> <scope>test</scope> </dependency>
@SpringBootTest // 代表这是一个springboot测试类,会自动加载springboot主启动程序 @RunWith(PowerMockRunner.class) //使用powermock本身的Runner @PowerMockRunnerDelegate(SpringRunner.class) //将powermock整合到spring容器中 @PowerMockIgnore({"javax.*.*", "com.sun.*", "org.xml.*", "org.apache.*"}) @PrepareForTest({HSSFWorkbook.class,HSSFCellStyle.class}) public class Demo { @Test public void test() throws Exception { EmployeeService service = PowerMockito.mock(EmployeeService.class); PowerMockito.when(service.hello()).thenReturn(999); int result = service.hello(); Assert.assertEquals(999, result); } }
下面主要对上面几个注解作相关解释:
@SpringBootTest:代表这是一个springboot测试类,会自动加载springboot主启动程序
@RunWith(PowerMockRunner.class): 使用powermock本身的Runner
@PowerMockRunnerDelegate(SpringRunner.class): 将powermock整合到spring容器中
@PowerMockIgnore({"javax.*
.*
", "com.sun.", "org.xml.", "org.apache.*"}) : 这个注解很重要,这也是powermock2.0.0与1.x版本重大不同的地方,由于powermock自带一个类加载器,使用该注解来禁止powermock类加载器加载一些类,避免和JVM类加载器冲突
@PrepareForTest({HSSFWorkbook.class,HSSFCellStyle.class}): 这个注解是告诉PowerMock为我提早准备一个xxx的class,根据我测试预期的行为去准备
至此,springboot和powermock的整合就完成了!
首先来看一段代码:不使用@PrepareForTest
@SpringBootTest @RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(SpringRunner.class) @PowerMockIgnore({"javax.*.*", "com.sun.*", "org.xml.*", "org.apache.*"}) // @PrepareForTest({HSSFWorkbook.class,HSSFCellStyle.class}) 不使用该注解 public class Demo { @Test public void test() throws Exception{ HSSFWorkbook wb = new HSSFWorkbook(); wb.createSheet(); } }
程序运行成功!
使用@PrepareForTest
@SpringBootTest @RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(SpringRunner.class) @PowerMockIgnore({"javax.*.*", "com.sun.*", "org.xml.*", "org.apache.*"}) @PrepareForTest({HSSFWorkbook.class}) public class Demo { @Test public void test() throws Exception{ HSSFWorkbook wb = new HSSFWorkbook(); wb.createSheet(); } }
咱们在test()测试中彻底没有用到powermock,可是为何会失败呢?
缘由:@PrepareForTest中的HSSFWorkbook.class,会告诉powermock提早准备这个类文件,那么当程序执行的时候,须要的该类的时候,就会使用到powermock准备的类
到目前为止,读者可能会认为 HSSFWorkbook wb = new HSSFWorkbook();
将会建立powermock准备的HSSFWorkbook对象,那么我debug程序,一探究竟
能够看到,这里new HSSFWorkbook()对象彻底是一个正常的对象,而非powermock的对象,而且在该类中使用的也是这个真对象
直到运行到MockGateway这个类 才出现问题,在powermock中会有大量的代理类,拦截器,这些类中会使用到pokwermock的HSSFWorkbook的对象,而非真正的HSSFWorkbook对象,所以会出现问题
一个私有类是彻底能够powermock的,那么是否是全部的类均可以powermock吗?
答案是:否认的()
@SpringBootTest @RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(SpringRunner.class) @PowerMockIgnore({"javax.*.*", "com.sun.*", "org.xml.*", "org.apache.*"}) @PrepareForTest({HSSFWorkbook.class}) public class Demo { @Test public void test3() throws Exception{ PowerMockito.mock(HSSFCellStyle.class); } }
定位至HSSFCellStyle 133行
咱们发现这是一个编译期常量,跟ThreadLocal,是没办法跟powermock对象一块儿建立的
切记:powermock对象是没法改变编译期常量的
@InjectMocks private SomeHandler someHandler; @Mock 或者 @Spy private OneDependency oneDependency; // 此mock将被注入到someHandler
这里的@InjectMocks和@Autowired功能完彻底全同样,惟一不一样的是,@InjectMocks可使oneDependency这个Mock对象自动注入到someHandler这个对象中。注意:①@InjectMocks所表示的对象及someHandler是一个普通的对象 ②Mock所表示的对象及oneDependency是一个Mock对象
@MockBean 会被装配到相关的类中 代替@Autowired
@Mock 不会被装配到相关的类中 没法代替@Autowired
Mockito.when(alarmRulesDao.changeAlarmLevel(Mockito.anyInt(),Mockito.anyInt())) .thenReturn(-1); Integer changeNumber = alarmRulesDao.changeAlarmLevel(changeAlarmlevelRequest.getId(), changeAlarmlevelRequest.getAlarmLevel());
即便Mock了changeAlarmLevel方法,其中的
changeAlarmlevelRequest.getId() changeAlarmlevelRequest.getAlarmLevel()
仍是会正常执行的
this.getHSSFWorkbook(downloadVO.getSheetName(), downloadList));
mock的时候不能
Mockito.anyString(),Mockito.anyList()
而要
Mockito.any(),Mockito.anyList() 由于mock对象中的参数执行了相关运算
参考资料:
https://zhidao.baidu.com/question/390585793246337165.html
http://www.javashuo.com/article/p-anejzaeq-ke.html
http://www.managershare.com/post/355904
http://www.javashuo.com/article/p-xjcubdfo-bh.html
https://qicen.iteye.com/blog/1928257