阿毛
It's me !
想你所想

Mockito之mock方法抛出受检查异常

基友偶然间遇到一个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!”

https://file.blog.humh.cn/2021/01/d2b5ca33bd970f64a6301fa75ae2eb22.png

原因分析及解决

我们知道IOException属于Java Exception异常中的受检查异常(Checked Exception)。受检查机制在于,如果代码在运行中需要抛出受检查异常,则对应方法签名后必须声明式的再抛出,即throws IOException,或者同代码块内catch。而RuntimeException不用。如java.io包下的大多数方法一样,会在方法上标注throws IOException,这样做的目的就是让调用者知道这个方法可能存在网络连接、文件不存在等导致的IO异常,在代码运行前要做好这方面逻辑的检查!这也是Java受检查异常设计初的目的,它会在代码编译期就开始生效!(语言规则上允许catch,但总不能这样没有意义的设计,异常的处理应该由下游调用者进行)换言之,受检查机制就是,需要调用者运行之前提前检查,并做好准备,不会让调用者没有准备的情况下,承担异常风险!

同时注意明确catch异常类型为受检查异常的前提是,try中的代码必须显示抛出对应受检查异常,否则不能明确catch。不难理解,毕竟作用于编译期的异常。

https://file.blog.humh.cn/2021/01/d2b5ca33bd970f64a6301fa75ae2eb22-1.png

回到“问题描述”中的代码,方法本身并没有throws(被调用方法内catch在mock时也没用),既然没有告知调用者该方法可能抛出受检查异常,则调用者不承担责任,也就不允许运行过程中再抛出此类异常,抛出则违背机制。所以mock运行时,就会出现这样的错误。

如果要想某个方法在mock中抛出受检查异常,则必须提前在方法上throws声明好 。

public void test() throws IOException{}
https://file.blog.humh.cn/2021/01/d2b5ca33bd970f64a6301fa75ae2eb22-2.png

如果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写法)仍可以完成抛出受检查异常的工作,结果看起来和“原因分析与解决”中的结果一样。

https://file.blog.humh.cn/2021/01/d2b5ca33bd970f64a6301fa75ae2eb22-3.png

但需要注意!这仅仅是结果一样,他们的处理过程是不一样的,区别在于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受检查异常的机制。

humh

文章作者

站长本人,一个憨批!

发表回复

textsms
account_circle
email

想你所想

Mockito之mock方法抛出受检查异常
基友偶然间遇到一个mock受检查异常的问题,这里实践后总结一下。 这里使用的mockito进行UnitTest的mock操作。 问题描述 已在测试类中定义如下mock bean @MockBean(name = "u…
扫描二维码继续阅读
2021-01-04