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));