Java单元测试实践-13.Spy后Stub Spring的@Component组件

Java单元测试实践-00.目录(9万多字文档+700多测试示例)
https://blog.csdn.net/a82514921/article/details/107969340

1. Spy后Stub Spring的@Component组件

Spring的@Component组件的Spy对象,未Stub的方法会执行真实方法。在使用Spy对象时,与使用原始对象效果类似。

1.1. 创建Spy对象

创建Spy对象时,可以使用Mockito.spy()方法或@Spy注解,如下所示:

Mockito.spy()方法可以生成@Autowired等注解注入的对象的Spy对象,通过以上方法获得的Spy对象中引用的成员变量已完成注入。示例如下,可参考示例TestSpSpyNoAnnotation类test1方法。

@Autowired
private TestPublicNonVoidService1 testPublicNonVoidService1;

TestPublicNonVoidService1 testPublicNonVoidService1Spy = Mockito.spy(testPublicNonVoidService1);

Mockito.spy()方法可以生成通过反射获取的对象的Spy对象,通过以上方法获得的Spy对象中所引用的成员变量已完成注入。示例如下,可参考示例TestSpSpyNoAnnotation类test2方法。

TestPublicNonVoidService1 testPublicNonVoidService1In = Whitebox.getInternalState(testService2,
        TestPublicNonVoidService1.class);

TestPublicNonVoidService1 testPublicNonVoidService1Spy = Mockito.spy(testPublicNonVoidService1In);

由于Spring中的Bean默认是单例的,各个类中注入的同一个类的对象都是同一个,因此在测试代码中通过@Autowired等注解注入的某个类的对象,与通过反射从其他类中获取的该类的对象,都是同一个对象。可参考示例TestSpSpyNoAnnotation类test3方法。

使用@Spy注解生成Spy对象时,可以对调用构造函数创建的对象生成Spy对象,生成的Spy对象中引用的成员变量未完成注入,均为null。在执行Spy对象的真实方法时,若使用了未完成注入的成员变量,会出现空指针异常。示例如下,可参考示例TestSpSpyAnnotation类test1方法。

@Spy
private TestPublicNonVoidService1 testPublicNonVoidService1AtSpy1 = new TestPublicNonVoidService1Impl();

使用@Spy注解生成Spy对象时,可以对通过@Autowired等注解注入的对象生成Spy对象,生成的Spy对象中引用的成员变量已完成注入。示例如下,可参考示例TestSpSpyAnnotation类test2方法。

@Spy
@Autowired
private TestPublicNonVoidService1 testPublicNonVoidService1AtSpy2;

使用@Spy注解生成Spy对象时,若指定生成接口类的Spy对象,在执行测试时会失败,提示“classMethod FAILED”。示例如下,可参考示例TestSpSpyAnnotationWrong类。

@Spy
private TestPublicNonVoidService1 testPublicNonVoidService1AtSpy1;

异常信息示例如下:

org.mockito.exceptions.base.MockitoException: Cannot create a @Spy for 'testPublicNonVoidService1AtSpy1' field because the *instance* is missing
Example of correct usage of @Spy:
   @Spy List mock = new LinkedList();

1.2. Spy对象的Stub方法选择

参考 https://static.javadoc.io/org.mockito/mockito-core/latest/org/mockito/Mockito.html#spy-T- ,“Important gotcha on spying real objects!”。有时使用when()对Spy对象进行Stub是不可能或不切实际的。因此,对于Spy对象,建议始终使用doReturn|Answer|Throw|CallRealMethod系列方法进行Stub。

当使用Mockito.when()方法对Spy对象进行Stub时,会执行真实方法,示例如下。

@Autowired
protected TestPublicNonVoidService1 testPublicNonVoidService1;

protected TestPublicNonVoidService1 testPublicNonVoidService1Spy;

testPublicNonVoidService1Spy = Mockito.spy(testPublicNonVoidService1);

Mockito.when(testPublicNonVoidService1Spy.test1(TestConstants.FLAG1)).thenReturn(TestConstants.MOCKED);

关于对Spy对象进行Mockito.when()操作时执行真实方法的原因,已在前文静态方法部分进行过分析,不再重复。

为了避免在对Spy对象进行Stub时执行真实方法,应使用Mockito.do…().when()方法对Spy对象进行Stub。

以上可参考示例TestSpSPuNVThenAnswer、TestSpSPuNVThenCallRealMethod、TestSpSPuNVThenReturn、TestSpSPuNVThenThrow类。

1.3. Stub @Component组件Spy对象公有非void方法

  • 修改返回值

使用Mockito.doReturn().when(@Component组件的Spy对象).方法(Stub参数条件),支持对Spring的@Component组件的Spy对象的公有非void方法进行Stub,返回指定的值。可参考示例TestSpSPuNVThenReturn类。

  • 抛出异常

使用Mockito.doThrow().when(@Component组件的Spy对象).方法(Stub参数条件),支持对Spring的@Component组件的Spy对象的公有非void方法进行Stub,抛出指定的异常。可参考示例TestSpSPuNVThenThrow类。

  • 使用Answer实现回调

使用Mockito.doAnswer().when(@Component组件的Spy对象).方法(Stub参数条件),支持对Spring的@Component组件的Spy对象的公有非void方法进行Stub,实现回调。可参考示例TestSpSPuNVThenAnswer类。

  • 使用verify判断方法的执行次数

判断Spring的@Component组件的Spy对象的公有void方法执行次数,可使用Mockito.verify()方法,与判断Spring的@Component组件的Mock对象的公有非void方法执行次数时的步骤相同。可参考示例TestSpSPuNVVerify类。

  • 使用Captor获取调用参数

判断Spring的@Component组件的Spy对象的公有void方法执行次数,并使用Captor获取调用参数,可使用Mockito.verify()方法及ArgumentCaptor类,与处理Spring的@Component组件的Mock对象的公有非void方法时的步骤相同。可参考示例TestSpSPuNVVerifyCaptor类。

  • 执行真实方法

对于接口的Spy对象,不支持通过Mockito.doCallRealMethod().when()方法Stub,使其执行真实方法,会出现异常,可参考示例TestSpSPuNVThenCallRealMethod类test1方法。

对于实现类的Spy对象,支持通过Mockito.doCallRealMethod().when()方法Stub,使其执行真实方法。可参考示例TestSpSPuNVThenCallRealMethod类test3方法。

1.4. Stub @Component组件Spy对象公有void方法

  • 抛出异常

使用Mockito.doThrow().when(@Component组件的Spy对象).方法(Stub参数条件),支持对Spring的@Component组件的Spy对象的公有void方法进行Stub,抛出指定的异常。可参考示例TestSpSPuVThenThrow类。

  • 使用Answer实现回调

使用Mockito.doAnswer().when(@Component组件的Spy对象).方法(Stub参数条件),支持对Spring的@Component组件的Spy对象的公有void方法进行Stub,实现回调。可参考示例TestSpSPuVThenAnswer类。

  • 使用verify判断方法的执行次数

判断Spring的@Component组件的Spy对象的公有void方法执行次数,可使用Mockito.verify()方法,与判断Spring的@Component组件的Mock对象的公有非void方法执行次数时的步骤相同。可参考示例TestSpSPuVVerify类。

  • 使用Captor获取调用参数

判断Spring的@Component组件的Spy对象的公有void方法执行次数,并使用Captor获取调用参数,可使用Mockito.verify()方法及ArgumentCaptor类,与处理Spring的@Component组件的Mock对象的公有非void方法时的步骤相同。可参考示例TestSpSPuVVerifyCaptor类。

  • 执行真实方法

对于实现类的Spy对象,支持通过Mockito.doCallRealMethod().when()方法Stub,执行真实方法。可参考示例TestSpSPuVThenCallRealMethod类。

  • 什么也不做

使用Mockito.doNothing().when(@Component组件的Spy对象).方法(Stub参数条件),支持对Spring的@Component组件的Spy对象的公有void方法进行Stub,使其什么也不做。可参考示例TestSpSPuVDoNothing类。

1.5. Stub @Component组件Spy对象私有非void方法

Mockito不支持对私有方法进行Stub,需要使用PowerMockito对Spring的@Component组件Spy对象私有方法进行Stub。

通过反射调用私有方法,或通过对应的公有方法间接调用私有方法时,Stub均能生效。

在对Spring的@Component组件的Spy对象的私有方法进行Stub时,需要使用PowerMockito.spy()返回的Spy对象,以及PowerMockito.do…().when()方法,并使用@PrepareForTest注解指定私有方法对应的实现类。

  • 修改返回值

使用PowerMockito.doReturn().when(@Component组件的Spy对象, 方法名, Stub参数条件),支持对Spring的@Component组件的Spy对象的私有非void方法进行Stub,返回指定的值。可参考示例TestSpSPrNVThenReturn类。

  • 抛出异常

使用PowerMockito.doThrow().when(@Component组件的Spy对象, 方法名, Stub参数条件),支持对Spring的@Component组件的Spy对象的私有非void方法进行Stub,抛出指定的异常。可参考示例TestSpSPrNVThenThrow类。

  • 使用Answer实现回调

使用PowerMockito.doAnswer().when(@Component组件的Spy对象, 方法名, Stub参数条件),支持对Spring的@Component组件的Spy对象的私有非void方法进行Stub,实现回调,执行自定义操作。可参考示例TestSpSPrNVThenAnswer类。

  • 使用verify判断方法的执行次数

使用PowerMockito类的verifyPrivate()方法可以判断Spring的@Component组件的Spy对象的私有void方法执行次数,与判断Mock对象私有非void方法执行次数类似。可参考示例TestSpSPrNVVerify类。

  • 使用Captor获取调用参数

在使用PowerMockito.verifyPrivate()方法判断Spy对象的方法执行次数时,还可以使用Captor获取调用参数,与Mock对象私有非void方法的处理类似。可参考示例TestSpSPrNVVerifyCaptor类。

  • 执行真实方法

使用PowerMockito.doCallRealMethod().when(@Component组件的Spy对象, 方法名, Stub参数条件),支持对Spring的@Component组件的Spy对象的私有非void方法进行Stub,执行真实方法。可参考示例TestSpSPrNVThenCallRealMethod类。

1.6. Stub @Component组件Spy对象私有void方法

对Spring的@Component组件Spy对象私有void方法进行Stub的处理,与对Spring的@Component组件Spy对象私有非void方法进行Stub的处理类似。

  • 抛出异常

使用PowerMockito.doThrow().when(@Component组件的Spy对象, 方法名, Stub参数条件),支持对Spring的@Component组件的Spy对象的私有void方法进行Stub,抛出指定的异常。可参考示例TestSpSPrVThenThrow类。

  • 使用Answer实现回调

使用PowerMockito.doAnswer().when(@Component组件的Spy对象, 方法名, Stub参数条件),支持对Spring的@Component组件的Spy对象的私有void方法进行Stub,实现回调,执行自定义操作。可参考示例TestSpSPrVThenAnswer类。

  • 使用verify判断方法的执行次数

使用PowerMockito类的verifyPrivate()方法可以判断Spring的@Component组件的Spy对象的私有void方法执行次数,与判断Mock对象私有非void方法执行次数类似。可参考示例TestSpSPrVVerify类。

  • 使用Captor获取调用参数

在使用PowerMockito.verifyPrivate()方法判断Spy对象的方法执行次数时,还可以使用Captor获取调用参数,与Mock对象私有非void方法的处理类似。可参考示例TestSpSPrVVerifyCaptor类。

  • 执行真实方法

使用PowerMockito.doCallRealMethod().when(@Component组件的Spy对象, 方法名, Stub参数条件),支持对Spring的@Component组件的Spy对象的私有void方法进行Stub,执行真实方法。可参考示例TestSpSPrVThenCallRealMethod类。

  • 什么也不做

使用PowerMockito.doNothing().when(@Component组件的Spy对象, 方法名, Stub参数条件),支持对Spring的@Component组件的Spy对象的私有void方法进行Stub,使其什么也不做。可参考示例TestSpSPrVDoNothing类。

1.7. 未Stub的方法的返回值

对于Spring的@Component组件的Spy对象未Stub的方法,会执行真实方法,返回值为原始方法返回值。可参考示例TestSpSpyUnstubbedMethod类。

1.8. 被Stub方法条件不满足的返回值

对于Spring的@Component组件的Spy对象被Stub的方法,在执行时若参数不满足Stub条件,返回值与未被Stub的方法相同,返回原始方法返回值。可参考示例TestSpSpyStubNotSatisfied类。

1.9. Spy对象类名标志

Spy对象的类名中包含Mock标志,如“com.adrninistrator.service.impl.TestPublicNonVoidService1Impl$MockitoMock$456421731”,根据Spy对象的Class对象是否等于原始类Class对象可以判断其是否为Spy对象。可参考示例TestSpSpyClassFlag类。

1.10. 同一个类的多个Spy对象

使用Mockito.spy()方法或@Spy注解可以对同一个类产生多个Spy对象,每个Spy对象之间相互独立,不会相互影响。可参考示例TestSpSpyMulti类。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值