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

news/2024/7/7 10:03:53

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

1. Spring Context加载次数

在使用PowerMock时,每执行一个测试类,Spring Context都会重新加载一次,不会使用缓存。

参考“A question on PowerMock setup”( https://github.com/powermock/powermock/issues/800 ),有人尝试在使用PowerMock时重用Spring Context,PowerMock的维护者回复建议将DefaultCacheAwareContextLoaderDelegate类添加至@PowerMockIgnore中。有其他人回复上述方法会导致损坏依赖树并最终放弃。

经过测试,以上方法不可行,在程序启动时会报错无法启动,与上述issue中的回复一致。

按照实际情况考虑,在使用Mock时,假如重用Spring Conetxt,会导致之前设置的Mock条件对后续执行的测试产生影响,因此重用Spring Context没有必要,执行每个测试类时都重新加载Spring Context更合理。

2. Mock后Stub Spring的@Component组件

以下针对Spring的@Component组件的Mock对象进行Stub操作进行说明。

2.1. 创建Mock对象

创建Mock对象时,可以使用Mockito.mock()方法或@Mock注解,示例如下。

TestPublicNonVoidService1 testPublicNonVoidService1 = Mockito.mock(TestPublicNonVoidService1.class);

@Mock
private TestPublicNonVoidService1 testPublicNonVoidService1Mock;

使用@Mock注解的Mock对象,与Mockito.mock()返回的Mock对象的方法均支持Stub,效果相同。

2.2. Mock对象类名标志

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

2.3. 同一个类的多个Mock对象

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

2.4. Stub @Component组件Mock对象公有非void方法

对Spring的@Component组件的Mock对象公有非void方法进行Stub时,与对静态方法的处理类似。

2.4.1. 修改返回值

使用Mockito.when(@Component组件的Mock对象.方法(Stub参数条件)).thenReturn(),thenReturn()方法的参数指定了返回值。支持对Spring的@Component组件的Mock对象的公有非void方法进行Stub,返回指定的值。当调用被Stub的方法时,真实方法不会被调用。示例如下,可参考示例TestSpMPuNVThenReturn类。

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

2.4.2. 抛出异常

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

Mockito.when(testPublicNonVoidService1.test1(TestConstants.FLAG1)).thenThrow(new RuntimeException
        (TestConstants.MOCKED));

2.4.3. 使用Answer实现回调

使用Mockito.when(@Component组件的Mock对象.方法(Stub参数条件)).thenAnswer(),支持对Spring的@Component组件的Mock对象的公有非void方法进行Stub,实现回调,执行自定义操作。示例如下,可参考示例TestSpMPuNVThenAnswer类。

Mockito.when(testPublicNonVoidService1.test1(TestConstants.FLAG1)).thenAnswer(invocation -> TestConstants
        .MOCKED);

使用Answer时支持的功能,可参考前文对静态公有非void方法的处理,对于执行真实方法的处理,可以参考后文。

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

使用Mockito.verify(@Component组件的Mock对象, VerificationMode对象).方法(Verify参数条件),可以判断Spring的@Component组件的Mock对象的公有非void方法执行次数。示例如下,可参考示例TestSpMPuNVVerify类。

Mockito.verify(testPublicNonVoidService1, Mockito.times(1)).test1(Mockito.anyString());

Mockito.verify(testPublicNonVoidService1, Mockito.times(1)).test1(TestConstants.FLAG1);

VerificationMode对象的使用方法,可参考前文对静态公有非void方法的处理。

2.4.4.1. 使用Captor获取调用参数

在使用Mockito.verify()方法判断Mock对象的方法执行次数时,还可以使用Captor获取调用参数,需要将Verify参数条件替换为ArgumentCaptor类的capture()方法返回的对象,如Mockito.verify(@Component组件的Mock对象, VerificationMode对象).方法(ArgumentCaptor对象.capture())。示例如下,可参考示例TestSpMPuNVVerifyCaptor类。

ArgumentCaptor<String> argCaptor1 = ArgumentCaptor.forClass(String.class);

Mockito.verify(testPublicNonVoidService1, Mockito.times(1)).test1(argCaptor1.capture());

ArgumentCaptor对象的使用方法,可参考前文对静态公有非void方法的处理。

2.4.5. 执行真实方法

使用Mockito.when(@Component组件的Mock对象.方法(Stub参数条件)).thenCallRealMethod(),支持对Spring的@Component组件的Mock对象的公有非void方法进行Stub,执行真实方法。

对于接口的Mock的某方法进行Stub,使其执行真实方法时,会出现异常。 异常信息如下所示,可参考示例TestSpMPuNVThenCallRealMethod类test1方法。

org.mockito.exceptions.base.MockitoException
Cannot call abstract real method on java object!
Calling real methods is only possible when mocking non abstract method.
  //correct example:
  when(mockOfConcreteClass.nonAbstractMethod()).thenCallRealMethod();

对于实现类的Mock对象,支持通过Mockito.when().thenCallRealMethod()方法Stub,执行真实方法。 示例如下,可参考示例TestSpMPuNVThenCallRealMethod类test2方法。

TestPublicNonVoidService1Impl testPublicNonVoidService1ImplMock = Mockito.mock(TestPublicNonVoidService1Impl.class);

Mockito.when(testPublicNonVoidService1ImplMock.test1(TestConstants.FLAG1)).thenCallRealMethod();

对实现类Mock对象的方法进行Stub,使其执行真实方法,若在真实方法中使用了需要注入的成员变量,由于需要注入的成员变量为null,会出现空指针异常。可参考示例TestSpMPuNVThenCallRealMethod类test3方法。

2.4.6. Stub同一个方法多次,每次执行不同的Stub操作

参考“12. doReturn()|doThrow()| doAnswer()|doNothing()|doCallRealMethod() family of methods”( https://static.javadoc.io/org.mockito/mockito-core/latest/org/mockito/Mockito.html#do_family_methods_stubs )。

使用doThrow(), doAnswer(), doNothing(), doReturn()和doCallRealMethod()方法,可以在Stub同一个方法多次时,在测试过程中更改Stub行为,每次执行不同的Stub操作。

使用Mockito.do…()方法支持对@Component组件的Mock对象的公有方法进行Stub,示例为Mockito.do…().when(@Component组件的Mock对象).方法(Stub参数条件)。

当对同一个方法Stub多次时,第n次执行的行为满足第n次Stub指定的操作,当执行次数大于Stub次数时,满足最后一次Stub指定的操作。

例如通过以下方式对同一个方法Stub两次,使其每次返回不同的值,多次执行时返回值为TestConstants.FLAG1、TestConstants.FLAG2、TestConstants.FLAG2…,可参考示例TestSpMPuNVThenReturnMulti类。

Mockito.doReturn(TestConstants.FLAG1).doReturn(TestConstants.FLAG2).when(testPublicNonVoidService1).test1
        (Mockito.anyString());

可以使用不同的do…方法对同一个方法Stub多次。

例如执行Mockito.doNothing().doThrow().when(),可以使被Stub的方法第一次执行时什么也不做,第二次执行时抛出异常。可参考示例TestSpMPuVDoNothing类test2方法。

2.5. Stub @Component组件Mock对象公有void方法

参考“12. doReturn()|doThrow()| doAnswer()|doNothing()|doCallRealMethod() family of methods”( https://static.javadoc.io/org.mockito/mockito-core/latest/org/mockito/Mockito.html#do_family_methods_stubs )。

对void方法进行Stub需要使用与Mockito.when().then…()不同的方法,因为编译器不支持在括号内指定void方法(对应Mockito.when()方法的参数)。

可以使用doThrow(), doAnswer(), doNothing(), doReturn()和doCallRealMethod()方法替代对应的then…()方法。

例如使用doThrow()方法时,示例如下:

Mockito.doThrow(new RuntimeException()).when(mockedList).clear();

使用PowerMockito.when(@Component组件的Mock对象, 方法名, Stub参数条件).then…()方法,也支持对@Component组件公有void方法进行Stub,示例如下:

PowerMockito.when(testPublicVoidService1, TestPublicVoidService1Impl.NAME_TEST1, Mockito.any(StringBuilder
        .class)).thenThrow(new RuntimeException(TestConstants.MOCKED));

2.5.1. 抛出异常

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

Mockito.doThrow(new RuntimeException(TestConstants.MOCKED)).when(testPublicVoidService1).test1(Mockito.any
        (StringBuilder.class));

2.5.2. 使用Answer实现回调

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

PowerMockito.doAnswer(invocation -> {
    invocation.callRealMethod();
    return null;
}).when(testPublicVoidService1).test1(Mockito.any(StringBuilder.class));

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

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

2.5.3.1. 使用Captor获取调用参数

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

2.5.4. 执行真实方法

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

org.mockito.exceptions.base.MockitoException
Cannot call abstract real method on java object!
Calling real methods is only possible when mocking non abstract method.
  //correct example:
  when(mockOfConcreteClass.nonAbstractMethod()).thenCallRealMethod();

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

Mockito.doCallRealMethod().when(testPublicVoidService1).test1(Mockito.any(StringBuilder.class));

若实现类的Mock对象执行的真实方法中使用了需要注入的成员变量,其值会是null,执行真实方法时会出现空指针异常,示例略。

2.5.5. 什么也不做

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

Mockito.doNothing().when(testPublicVoidService1).test1(Mockito.any(StringBuilder.class));

2.6. Stub @Component组件Mock对象私有非void方法

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

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

2.6.1. Mockito.mock()返回的Mock对象

当使用PowerMockito.when()方法对Mockito.mock()返回的Mock对象的私有方法进行Stub时,会出现异常。异常示例如下,可参考示例TestSpMPrNVThenReturnMockito类test1方法。

org.mockito.exceptions.misusing.InvalidUseOfMatchersException
Misplaced or misused argument matcher detected here:
You cannot use argument matchers outside of verification or stubbing.

当使用PowerMockito.do…().when()方法对Mockito.mock()返回的Mock对象的私有方法进行Stub时,不会出现异常,但Stub会失败。可参考示例TestSpMPrNVThenReturnMockito类test2方法。

2.6.2. 使用@PrepareForTest注解

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

若不使用@PrepareForTest注解指定私有方法对应的实现类,对私有方法的Stub不会生效,且测试方法执行完毕后会出现失败,出现以下提示信息。可参考示例TestSpMPrNVThenReturnNoPrepare类。

Test mechanism.classMethod FAILED
org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Misplaced or misused argument matcher detected here:
You cannot use argument matchers outside of verification or stubbing.

2.6.3. 修改返回值

使用PowerMockito.doReturn().when(@Component组件的Mock对象, 方法名, Stub参数条件),doReturn()方法的参数指定了返回值。支持对Spring的@Component组件的Mock对象的私有非void方法进行Stub,返回指定的值。当调用被Stub的方法时,真实方法不会被调用。示例如下,可参考示例TestSpMPrNVThenReturn类。

PowerMockito.doReturn(TestConstants.MOCKED).when(testPrivateNonVoidService1, TestPrivateNonVoidService1Impl
        .NAME_TEST1, Mockito.anyString());

2.6.4. 抛出异常

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

PowerMockito.doThrow(new RuntimeException(TestConstants.MOCKED)).when(testPrivateNonVoidService1,
        TestPrivateNonVoidService1Impl.NAME_TEST1, Mockito.anyString());

2.6.5. 使用Answer实现回调

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

PowerMockito.doAnswer(invocation -> TestConstants.MOCKED).when(testPrivateNonVoidService1,
        TestPrivateNonVoidService1Impl.NAME_TEST1, Mockito.anyString());

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

使用PowerMockito类的verifyPrivate()方法可以判断Spring的@Component组件的Mock对象的私有非void方法执行次数,需要使用@PrepareForTest注解指定被Mock的类,分两步执行:

  • 首先执行“PowerMockito.verifyPrivate(@Component组件的Mock对象, VerificationMode对象);”
  • 再通过反射执行Spring的@Component组件的Mock对象的私有非void方法,参数使用Verify参数条件

示例如下,可参考示例TestSpMPrNVVerify类。

PowerMockito.verifyPrivate(testPrivateNonVoidService1, Mockito.times(0));
Whitebox.invokeMethod(testPrivateNonVoidService1, TestPrivateNonVoidService1Impl.NAME_TEST1, "");

在通过反射执行Spring的@Component组件的Mock对象的私有非void方法时,参数可为具体值,也可以使用Mockito.any()等ArgumentMatchers类的方法,可参考判断静态方法执行次数的说明。

当使用Mockito.verify()方法判断Spring的@Component组件的Mock对象的私有非void方法执行次数时,Mockito.verify()方法执行一次时正常,执行超过一次时会出现异常。异常信息如下所示,可参考示例TestSpMPrNVVerifyMockito类。

org.mockito.exceptions.misusing.UnfinishedVerificationException
Missing method call for verify(mock) here:

当使用PowerMockito.verifyPrivate()方法判断Spring的@Component组件的Mock对象的私有非void方法执行次数时,需要使用@PrepareForTest注解指定被Mock的类。 若不使用@PrepareForTest注解指定被Mock的类,PowerMockito.verifyPrivate()方法执行一次时正常,执行超过一次时会出现异常。异常信息如下所示,可参考示例TestSpMPrNVVerifyNoPrepare类。

org.mockito.exceptions.misusing.UnfinishedVerificationException
Missing method call for verify(mock) here:

2.6.6.1. 使用Captor获取调用参数

在使用PowerMockito.verifyPrivate()方法判断Mock对象的方法执行次数时,还可以使用Captor获取调用参数,需要将Verify参数条件替换为ArgumentCaptor类的capture()方法返回的对象。示例如下,可参考示例TestSpMPrNVVerifyCaptor类。

ArgumentCaptor<String> argCaptor1a = ArgumentCaptor.forClass(String.class);

PowerMockito.verifyPrivate(testPrivateNonVoidService1, Mockito.times(1));
Whitebox.invokeMethod(testPrivateNonVoidService1, TestPrivateNonVoidService1Impl.NAME_TEST1,
        argCaptor1a.capture());

2.6.7. 执行真实方法

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

PowerMockito.doCallRealMethod().when(testPrivateNonVoidService1, TestPrivateNonVoidService1Impl
        .NAME_TEST1, Mockito.anyString());

对于Spring的@Component组件的Mock对象,在执行真实方法时可能出现空指针异常,原因可参考前文。

2.7. Stub @Component组件Mock对象私有void方法

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

  • 抛出异常

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

  • 使用Answer实现回调

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

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

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

  • 使用Captor获取调用参数

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

  • 执行真实方法

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

对于Spring的@Component组件的Mock对象,在执行真实方法时可能出现空指针异常,原因可参考前文。

  • 什么也不做

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

PowerMockito.doNothing().when(testPrivateVoidService1, TestPrivateVoidService1Impl.NAME_TEST1, Mockito.any
        (StringBuilder.class));

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

相关文章

Java单元测试实践-12.Answer与未Stub的Spring组件方法

Java单元测试实践-00.目录&#xff08;9万多字文档700多测试示例&#xff09; https://blog.csdn.net/a82514921/article/details/107969340 1. Answer与未Stub的Spring组件方法 1.1. 未Stub的方法的返回值 对于Spring的Component组件的Mock对象未Stub的方法&#xff0c;返回…

一本书,一个人

本文转自 http://smartcard.blog.com/tag/%E7%8E%8B%E7%88%B1%E8%8B%B1/ 国内的智能卡发展&#xff0c;可以上溯到上个世纪90年代初期。当时大家对于磁卡都知之甚少&#xff0c;更不要说IC卡、智能卡了。由于那段时间国际通用的智能卡标准还没有怎么得到很好地贯彻执行&#xf…

不受环境干扰,这套声学全息方案实现了虚实交互

一谈到全息显示&#xff0c;大家默认想到的就是光学全息方案&#xff0c;比如需要将光投射到某样东西上&#xff0c;比如视网膜&#xff0c;或是烟雾等介质上&#xff0c;才能成像。市面上一些常见的2D、3D全息方案&#xff0c;如全息风扇、Voxon全息系统等等&#xff0c;分别采…

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

Java单元测试实践-00.目录&#xff08;9万多字文档700多测试示例&#xff09; https://blog.csdn.net/a82514921/article/details/107969340 1. Spy后Stub Spring的Component组件 Spring的Component组件的Spy对象&#xff0c;未Stub的方法会执行真实方法。在使用Spy对象时&am…

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

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

俺是一个三俗的程序员

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

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

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