在单元测试过程中有时候Mock框架是必不可少的,通过Mock框架可以用来模拟对象的行为。这里我们以目前主流的Mockito框架进行介绍
POM依赖
这里我们向POM中引入Mockito依赖,同时这里对于Junit框架,我们选用Junit 5版本
1 | <!-- Junit --> |
初探
引入依赖后我们来看看如何使用该框架来Mock对象,并对相关方法进行打桩。不难看出,其基本用法还是很简单的。如果Mock对象的方法被打桩了,则调用时会返回指定值;反之则会按照方法声明的返回值类型返回相应的空值。换言之,对于Mock而言,Mock的目标既可以是类、也可以是接口
1 | public class MockDemo1 { |
参数匹配
在对方法进行打桩的过程中,我们还可以进行模糊匹配参数值。故在ArgumentMatchers类中内置了很多常见的参数匹配器。当然如果内置的参数匹配器无法满足时,我们还可以通过实现ArgumentMatcher函数式接口来自定义参数匹配器。然后通过argThat方法传入自定义参数匹配器即可
1 | public class MockDemo1 { |
then 系列方法
thenReturn 方法
通过thenReturn可以实现对方法进行打桩以返回指定值
1 | public class MockDemo1 { |
thenThrow 方法
通过thenThrow可以实现对方法进行打桩,以实现对方法调用抛出指定异常
1 | public class MockDemo1 { |
then、thenAnswer 方法
通过then可以实现对方法进行打桩,以实现自定义返回值逻辑。具体地我们只需实现函数式接口Answer,并实现自定义返回值逻辑即可。特别地,then 方法 和 thenAnswer 方法作用是相同的
1 | public class MockDemo1 { |
do 系列方法
doReturn 方法
与thenReturn方法作用类似,doReturn 方法也可以实现对方法进行打桩以返回指定值
1 | public class MockDemo1 { |
doThrow 方法
与thenThrow方法作用类似,doThrow 方法也可以实现对方法进行打桩,以实现对方法调用抛出指定异常
1 | public class MockDemo1 { |
doAnswer 方法
与then、thenAnswer方法作用类似,doAnswer 方法也可以实现对方法进行打桩。以实现自定义返回值逻辑。具体地我们只需实现函数式接口Answer,并实现自定义返回值逻辑即可
1 | public class MockDemo1 { |
doNothing 方法
doNothing 方法可以对Void Method空方法进行打桩,其中所谓Void Method空方法指的是返回类型为void的方法。显然,对于Mock对象的Void Method空方法而言,没有打桩的必要。因为不打桩也不会调用真实的方法逻辑。故其更多的应用场景是对Spy对象的Void Method空方法进行打桩,使得当调用Void Method时,不会再调用真实的方法
1 | public class MockDemo1 { |
测试结果如下,可以看到Void Method空方法一旦被打桩掉之后。原方法就不会再执行了,控制台也不会输出了
reset 方法
reset 方法可以重置Mock、Spy的对象,移除对其的所有打桩
1 | public class MockDemo1 { |
Spy对象
与Mock对象是模拟的不同,Spy出来的对象是一个真实的对象。所以对于Spy出来的对象而言,如果调用未打桩的方法, 其会调用真实的方法。并根据调用结果返回真实的返回值;反之,如果Spy对象的方法被打桩了,则后续调用该方法时,真实方法将不会再被被调用,并返回打桩后值。这里特别说明下,在对Spy对象进行打桩过程中。如果使用then系列方法进行打桩, 则会导致真实方法在打桩过程中被调用;而如果使用do系列方法进行打桩, 则真实方法在打桩过程中不会被调用
1 | public class SpyDemo1 { |
then系列方法、do系列方法 区别
虽然then系列方法、do系列方法的作用上是相同的。但在某些场景时可能无法使用then系列方法,使得我们只能使用do系列方法
对Spy对象进行打桩
前面提到,在对Spy对象进行打桩过程中。如果使用then系列方法进行打桩, 则会导致真实方法在打桩过程中被调用。此时即可能会导致打桩失败。这时则应该使用do系列方法进行打桩
1 | public class DoThenDiff { |
测试结果如下所示
对Void Method进行打桩
then系列方法无法对Void Method方法进行打桩,其会导致无法通过编译。故此时也只能选择do系列方法进行打桩
1 | public class DoThenDiff { |
基于注解的模拟
前面我们通过都是Mockito的mock、spy方法来模拟对象。事实上,我们还可以通过@Mock、@Spy注解标识其是一个Mock、Spy的对象。为了让这两个注解生效、并生成相应的对象。我们有两种方式实现
- 方式1:在测试类上添加 @ExtendWith(MockitoExtension.class) 注解
- 方式2:在测试类的初始化方法添加 MockitoAnnotations.initMocks(this) 实现
进一步地,我们还可以使用 @InjectMocks 注解以实现将@Mock、@Spy修饰的对象自动注入到@InjectMocks修饰的对象中。其支持字段注入、构造器注入、set方法注入
1 | // 使@Mock、@Spy注解生效 方式1 |