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类。