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

news/2024/7/8 12:53:40

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


http://www.niftyadmin.cn/n/4057560.html

相关文章

Java单元测试实践-15.Stub、Replace、Suppress Spring的方法

Java单元测试实践-00.目录(9万多字文档700多测试示例) https://blog.csdn.net/a82514921/article/details/107969340 1. Stub、Replace、Suppress Spring的方法 对Spring的Component组件的方法进行Stub、Replace、Suppress时,与对Mock/Spy对…

俺是一个三俗的程序员

俺很三俗,想起来一定尽量回复过来。 想起来一个,就是在水园天天有人发大道理,俺每次看见心里骂,废话一堆。 看见有人忽悠新技术,心里骂,不就是技术垄断吗? 收到垃圾邮件,让信X的&…

Java单元测试实践-16.Spring AOP与Mock

Java单元测试实践-00.目录(9万多字文档700多测试示例) https://blog.csdn.net/a82514921/article/details/107969340 1. Spring AOP与Mock 以下示例使用CGLIB代理,或JDK动态代理,执行结果相同。 以下使用注解的方式设置AOP&…

Java单元测试实践-17.Mybatis与Mock

Java单元测试实践-00.目录(9万多字文档700多测试示例) https://blog.csdn.net/a82514921/article/details/107969340 1. Mybatis与Mock 在对Mybatis的Mapper对象进行处理时,可能需要使某个Mapper对象在某些情况下返回指定值,在某…

Java单元测试实践-18.使用注解进行Stub、Replace、Suppress

Java单元测试实践-00.目录(9万多字文档700多测试示例) https://blog.csdn.net/a82514921/article/details/107969340 1. 使用MockPolicy注解进行Stub、Replace、Suppress MockPolicy注解的说明可参考 https://javadoc.io/doc/org.powermock/powermock-…

俺是郭德纲先生的忠实非现场 听众+观众

俺从小喜欢听相声,在郭先生火爆之前就常年到处下载,曾经将某网站所有的相声都下载下来过。在俺看来,郭德纲先生的相声说的非常好,能够将俺逗乐,这说明郭德纲先生干一行爱一行、说相声首先就是搞笑的。 在郭先生被jb台陷…

回了一趟老家

周末回了一趟老家,又节能减排限电了,家里有两个鸵鸟蛋,可惜忘了照照片了,也忘了带过来了,家里养的几盆菊花开的很盛。 听说家里买了几根竹杆,到地里锯了几个竹根过来,不是很好,但是经…