Mockito之mock方法抛出受检查异常;Mockito 使用 doThrow 让方法抛出异常

1、如果一个对象的方法的返回值是 void,那么不能用 when … thenThrow 让该方法抛出异常。

(1)如果有返回值,下面这种写法是错误的:
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import static org.mockito.Mockito.when;

public class MockitoDemo {

    static class ExampleService {

        public void hello() {
            System.out.println("Hello");
        }

    }

    @Mock
    private ExampleService exampleService;

    @Test
    public void test() {

        MockitoAnnotations.initMocks(this);

        // 这句编译不通过,IDE 也会提示错误,原因很简单,when 的参数是非 void
        when(exampleService.hello()).thenThrow(new RuntimeException("异常"));

    }

}
(2)用 doThrow 可以让返回void的函数抛出异常,换成下面的写法即可:
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import static org.mockito.Mockito.doThrow;

public class MockitoDemo {

    static class ExampleService {

        public void hello() {
            System.out.println("Hello");
        }

    }

    @Mock
    private ExampleService exampleService;

    @Test
    public void test() {

        MockitoAnnotations.initMocks(this);

        // 这种写法可以达到效果
        doThrow(new RuntimeException("异常")).when(exampleService).hello();

        try {
            exampleService.hello();
            Assert.fail();
        } catch (RuntimeException ex) {
            Assert.assertEquals("异常", ex.getMessage());
        }

    }

}
也可以用 doThrow 让返回非void的函数抛出异常:
import org.junit.Assert;
import org.junit.Test;
import org.mockito.MockitoAnnotations;

import java.util.Random;

import static org.mockito.Mockito.*;

public class MockitoDemo {

    @Test
    public void test() {

        MockitoAnnotations.initMocks(this);

        Random random = mock(Random.class);

        // 下面这句等同于 when(random.nextInt()).thenThrow(new RuntimeException("异常"));
        doThrow(new RuntimeException("异常")).when(random).nextInt();

        try {
            random.nextInt();
            Assert.fail();
        } catch (RuntimeException ex) {
            Assert.assertEquals("异常", ex.getMessage());
        }

    }

}

2、使用的mockito进行UnitTest的mock操作,遇到的一个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!”
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受检查异常的机制。

转发自:
Mockito 使用 doThrow 让方法抛出异常
Mockito之mock方法抛出受检查异常

  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值