基友偶然间遇到一个mock受检查异常的问题,这里实践后总结一下。
这里使用的mockito进行UnitTest的mock操作。
问题描述
已在测试类中定义如下mock bean
@MockBean(name = "userProfileService") private UserProfileService userProfileService;
UserProfileService 中包含有这样一个方法
public void test() {}
测试类中,这样mock
@Test public void testMock() { try { Mockito.doThrow(new IOException()).when(userProfileService).test(); userProfileService.test(); } catch (Exception e) { System.out.println("eeeeeeeee"); } }
当运行上述代码时,会收到这样的异常:“Checked exception is invalid for this method!”
原因分析及解决
我们知道IOException属于Java Exception异常中的受检查异常(Checked Exception)。受检查机制在于,如果代码在运行中需要抛出受检查异常,则对应方法签名后必须声明式的再抛出,即throws IOException
,或者同代码块内catch
。而RuntimeException不用。如java.io包下的大多数方法一样,会在方法上标注throws IOException
,这样做的目的就是让调用者知道这个方法可能存在网络连接、文件不存在等导致的IO异常,在代码运行前要做好这方面逻辑的检查!这也是Java受检查异常设计初的目的,它会在代码编译期就开始生效!(语言规则上允许catch,但总不能这样没有意义的设计,异常的处理应该由下游调用者进行)换言之,受检查机制就是,需要调用者运行之前提前检查,并做好准备,不会让调用者没有准备的情况下,承担异常风险!
同时注意明确catch异常类型为受检查异常的前提是,try中的代码必须显示抛出对应受检查异常,否则不能明确catch。不难理解,毕竟作用于编译期的异常。
回到“问题描述”中的代码,方法本身并没有throws(被调用方法内catch在mock时也没用),既然没有告知调用者该方法可能抛出受检查异常,则调用者不承担责任,也就不允许运行过程中再抛出此类异常,抛出则违背机制。所以mock运行时,就会出现这样的错误。
如果要想某个方法在mock中抛出受检查异常,则必须提前在方法上throws声明好 。
public void test() throws IOException{}
如果mock抛出RuntimeException,则不需要任何处理。
拓展
public void test(){}
@Test public void testMock() { try { Mockito.doAnswer(invocation -> { throw new IOException(); }).when(userProfileService).test(); userProfileService.test(); } catch (Exception e) { System.out.println("eeeeeeeee"); } }
方法不变,即没有声明抛出任何异常的情况下,上面这段代码(doAnswer参数的写法为Answer接口匿名内部类实现的lambda写法)仍可以完成抛出受检查异常的工作,结果看起来和“原因分析与解决”中的结果一样。
但需要注意!这仅仅是结果一样,他们的处理过程是不一样的,区别在于doThrow和doAnswer两个方法本身的区别。
- doThrow:基本上用于在模拟对象内调用方法时要引发异常的情况,相当于异常抛出的位置源头是在方法内部,好比方法内部
throw new IOException
。 - doAnswer:将方法入参,拿来去执行answer() 方法内部的逻辑,然后将answer的执行结果作为mock的目标方法“输出”(返回值或抛异常),也就是相当于执行完目标方法,然后执行将answer() 的输出替代目标方法的输出,并不是在目标方法内完成。
所以上面doAnswer这种方式就相当于userProfileService.test() 方法不用声明抛异常,然后测试类中,
@Test public void testMock() { try { userProfileService.test(); throw new IOException(); } catch (Exception e) { System.out.println("eeeeeeeee"); } }
所以并没有违背Java受检查异常的机制。
发表评论