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

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));
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Classes contained in spring-mock.jar: org.springframework.mock.jndi.ExpectedLookupTemplate.class org.springframework.mock.jndi.SimpleNamingContext.class org.springframework.mock.jndi.SimpleNamingContextBuilder.class org.springframework.mock.web.DelegatingServletInputStream.class org.springframework.mock.web.DelegatingServletOutputStream.class org.springframework.mock.web.HeaderValueHolder.class org.springframework.mock.web.MockExpressionEvaluator.class org.springframework.mock.web.MockFilterChain.class org.springframework.mock.web.MockFilterConfig.class org.springframework.mock.web.MockHttpServletRequest.class org.springframework.mock.web.MockHttpServletResponse.class org.springframework.mock.web.MockHttpSession.class org.springframework.mock.web.MockMultipartFile.class org.springframework.mock.web.MockMultipartHttpServletRequest.class org.springframework.mock.web.MockPageContext.class org.springframework.mock.web.MockRequestDispatcher.class org.springframework.mock.web.MockServletConfig.class org.springframework.mock.web.MockServletContext.class org.springframework.mock.web.PassThroughFilterChain.class org.springframework.mock.web.portlet.MockActionRequest.class org.springframework.mock.web.portlet.MockActionResponse.class org.springframework.mock.web.portlet.MockMultipartActionRequest.class org.springframework.mock.web.portlet.MockPortalContext.class org.springframework.mock.web.portlet.MockPortletConfig.class org.springframework.mock.web.portlet.MockPortletContext.class org.springframework.mock.web.portlet.MockPortletPreferences.class org.springframework.mock.web.portlet.MockPortletRequest.class org.springframework.mock.web.portlet.MockPortletRequestDispatcher.class org.springframework.mock.web.portlet.MockPortletResponse.class org.springframework.mock.web.portlet.MockPortletSession.class org.springframework.mock.web.portlet.MockPortletURL.class org.springframework.mock.web.portlet.MockRenderRequest.class org.springframework.mock.web.portlet.MockRenderResponse.class org.springframework.test.AbstractDependencyInjectionSpringContextTests.class org.springframework.test.AbstractSingleSpringContextTests.class org.springframework.test.AbstractSpringContextTests.class org.springframework.test.AbstractTransactionalDataSourceSpringContextTests.class org.springframework.test.AbstractTransactionalSpringContextTests.class org.springframework.test.AssertThrows.class org.springframework.test.ConditionalTestCase.class org.springframework.test.annotation.AbstractAnnotationAwareTransactionalTests.class org.springframework.test.annotation.DirtiesContext.class org.springframework.test.annotation.ExpectedException.class org.springframework.test.annotation.IfProfileValue.class org.springframework.test.annotation.NotTransactional.class org.springframework.test.annotation.ProfileValueSource.class org.springframework.test.annotation.Repeat.class org.springframework.test.annotation.SystemProfileValueSource.class org.springframework.test.annotation.Timed.class org.springframework.test.jpa.AbstractAspectjJpaTests.class org.springframework.test.jpa.AbstractJpaTests.class org.springframework.test.jpa.OrmXmlOverridingShadowingClassLoader.class org.springframework.test.web.AbstractModelAndViewTests.class

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值