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

news/2024/11/13 1:24:20

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

1. Answer与未Stub的Spring组件方法

1.1. 未Stub的方法的返回值

对于Spring的@Component组件的Mock对象未Stub的方法,返回值为返回类型对应的默认值。与未Stub的静态方法的返回值情况类似。可参考示例TestSpMockUnstubbedMethod1类。

1.2. 未Stub的方法的返回值处理分析

生成Spring的@Component组件的Mock对象时会使用Mockito.mock()方法。

查看Mockito类的“public static <T> T mock(Class<T> classToMock)”方法,在其中调用了“public static <T> T mock(Class<T> classToMock, MockSettings mockSettings)”方法。后续处理可参考被Mock类的未Stub的静态方法的返回值处理分析。

1.3. 被Stub方法条件不满足的返回值

对于Spring的@Component组件的Mock对象被Stub的方法,在执行时若参数不满足Stub条件,返回值与未被Stub的方法相同,返回对应类型的默认值。可参考示例TestSpMockStubNotSatisfied1类。

1.4. 设置未Stub的方法的默认Answer

Mockito.mock()方法支持通过传入Answer或MockSettings对象设置默认的Answer,在执行Mock对象未Stub的方法时,由指定的默认Answer进行处理。

在调用Mockito.mock()方法方法时,若传入参数Answer对象,对应Mockito类的方法“public static <T> T mock(Class<T> classToMock, Answer defaultAnswer)”,示例如下:

Mockito.mock(TestPublicNonVoidService1.class, new CallsRealMethods());

在调用Mockito.mock()方法方法时,若传入参数MockSettings对象,需要通过defaultAnswer设置默认Answer,对应Mockito类的方法“public static <T> T mock(Class<T> classToMock, MockSettings mockSettings)”,示例如下:

Mockito.mock(TestPublicNonVoidService1.class, Mockito.withSettings().defaultAnswer(new CallsRealMethods()));

当Mock对象的非静态方法被Stub后,在执行该方法时受到对应的Stub控制,不再受默认Answer的影响,可参考示例TestSpMockDftAnsCallsRealMethodsImpl类test3方法。

1.4.1. 执行真实方法

使用Mockito.CALLS_REAL_METHODS、Answers.CALLS_REAL_METHODS、CallsRealMethods等作为默认Answer,可以使Mock对象未被Stub的非静态方法执行真实方法。在调用Mockito.mock()方法时,可以直接使用以上Answer,或使用Mockito.withSettings().defaultAnswer()方法及以上Answer,示例如下。

Mockito.mock(TestPublicNonVoidService1.class, Mockito.CALLS_REAL_METHODS);
Mockito.mock(TestPublicNonVoidService1.class, Answers.CALLS_REAL_METHODS);
Mockito.mock(TestPublicNonVoidService1.class, new CallsRealMethods());

Mockito.mock(TestPublicNonVoidService1.class, Mockito.withSettings().defaultAnswer(Mockito.CALLS_REAL_METHODS));
Mockito.mock(TestPublicNonVoidService1.class, Mockito.withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS));
Mockito.mock(TestPublicNonVoidService1.class, Mockito.withSettings().defaultAnswer(new CallsRealMethods()));

对于Spring的@Component组件接口的Mock对象,当设置默认Answer执行真实方法时,执行未Stub的非静态方法时,不会执行真实方法。可参考示例TestSpMockDftAnsCallsRealMethodsInterface类。

对于Spring的@Component组件实现类的Mock对象,当设置默认Answer执行真实方法时,执行未Stub的非静态方法时,会执行真实方法。可参考示例TestSpMockDftAnsCallsRealMethodsImpl类test1方法。

对于Spring的@Component组件实现类的Mock对象,使用执行真实方法的默认Answer,当执行真实方法时,若使用了需要注入的成员变量,由于需要注入的成员变量为null,会出现空指针异常。可参考示例TestSpMockDftAnsCallsRealMethodsImpl类test2方法。

在实际使用中,Spring的@Component组件实现类的Mock对象使用意义不大,因为其中需要注入的成员变量均为null,无法正常使用。若需要使Spring的@Component组件可以根据需要执行指定操作,或执行真实方法,可以使用Spy对象。

1.4.2. 抛出异常

使用ThrowsException作为默认Answer,可以使Mock对象未被Stub的非静态方法抛出指定异常,ThrowsException在创建时需要指定需要抛出的异常对象。如下所示,可参考示例TestSpMockDftAnsThrowsExpt1类。

TestPublicNonVoidService1 testPublicNonVoidService1 = Mockito.mock(TestPublicNonVoidService1.class, new ThrowsException(new RuntimeException(TestConstants.MOCKED)));

1.4.3. 什么也不做

使用DoesNothing作为默认Answer,可以使Mock对象未被Stub的非静态方法什么也不做,DoesNothing需要使用doesNothing()方法返回对象实例。如下所示,可参考示例TestSpMockDftAnsDoesNothing1类。

TestPublicNonVoidService1 testPublicNonVoidService1 = Mockito.mock(TestPublicNonVoidService1.class, DoesNothing.doesNothing());

1.4.4. 使用@Mock注解的answer参数

通过@Mock注解的answer参数,也可以指定Spring的@Component组件的Mock对象的默认Answer,answer参数类型为Answers,可以使用Answers枚举中的RETURNS_DEFAULTS等枚举值。当需要使Mock对象能够执行真实方法时,@Mock注解需要针对实现类设置。示例如下,可参考示例TestSpMockDftAnsAnnotationInterface、TestSpMockDftAnsAnnotationImpl类。

@Mock(answer = Answers.CALLS_REAL_METHODS)
private TestPublicNonVoidService1Impl testPublicNonVoidService1;

1.4.5. MockSettings的stubOnly属性

参考 https://static.javadoc.io/org.mockito/mockito-core/latest/org/mockito/MockSettings.html#stubOnly-- 。通过MockSettings接口的stubOnly()方法,可以指定生成的Mock对象仅用于对方法进行Stub,不记录方法调用,可以节省内存,但不允许进行调用验证(Mockito.verify())。默认情况下,stubOnly为false。

使用Mockito.mock()方法时,指定stubOnly属性的示例如下:

List stubOnly = Mockito.mock(List.class, withSettings().stubOnly());

@Mock注解中的stubOnly属性,也以起到相同的作用,使用@Mock注解时,指定stubOnly属性的示例如下:

@Mock(stubOnly = true)
private TestPublicNonVoidService1Impl testPublicNonVoidService1;

1.4.6. 委托方法调用

参考 https://static.javadoc.io/org.mockito/mockito-core/latest/org/mockito/AdditionalAnswers.html#delegatesTo-java.lang.Object- 。

使用AdditionalAnswers类的delegatesTo()方法可以生成一个将方法调用直接转发给被委托代表的Answer。被委托代表可以与Mock对象的类型相同或不同,若类型不相同,被委托代表中需要有与Mock对象相同的方法,否则会出现异常。

例如使用Mockito.mock()方法获得了A类的Mock对象,使用AdditionalAnswers.delegatesTo()方法可以指定A类的Mock对象,也可以使用与A类具有相同方法(方法名称、参数数量、类型、返回值均相同)的B类的对象。

对于使用常规Spy API难以进行Mock或Spy的Spy/部分Mock对象,使用AdditionalAnswers类的delegatesTo()方法很有帮助。可在以下场景使用:

  • 具有接口的final类;
  • 已被自定义代理的对象(如AOP、Mybatis的Mapper对象等);
  • 具有finalize方法的特殊对象( 即避免执行两次 )

使用Spy对象可以进行verify,使用delegatesTo不支持verify。

在使用AdditionalAnswers类的delegatesTo()方法生成Answer时,需要指定被委托代表。

使用Mockito.mock()方法生成Mock对象时,可以指定上述Answer,生成方法调用被委托的Mock对象并返回,AdditionalAnswers.delegatesTo()方法指定的对象为被委托代表。示例如下,可参考示例TestSpMockDelegatesTo1类

TestServiceA1 testServiceA1 = Mockito.mock(TestServiceA1.class, AdditionalAnswers.delegatesTo(new
        TestServiceA1Delegate()));

被委托代表可以使用Spring的@Component组件,代码需要保存在test模块中(防止对main模块产生影响),并在Spring配置文件的context:component-scan参数中添加对象的包名(或将包名设置为满足main模块中的Spring包扫描路径),使Spring对测试代码中的被委托代表以@Component组件的形式被扫描到。示例如下,可参考示例TestSpMockDelegatesTo2、TestServiceA1Delegate类。

@Autowired
private TestServiceA1Delegate testServiceA1Delegate;

TestServiceA1 testServiceA1 = Mockito.mock(TestServiceA1.class, AdditionalAnswers.delegatesTo(testServiceA1Delegate));

方法调用被委托的Mock对象,未被Stub的方法调用会被转发给被委托代表的对应的相同方法( 方法名称、参数数量、类型、返回值均相同 ),即调用被委托的Mock对象的方法时,会调用被委托代表的对应方法并返回。当间接调用被委托的方法时也能生效。例如以上生成的Mock对象testServiceA1,当调用其方法时,会调用被委托代表testServiceA1Delegate对象相同的方法。

方法调用被委托的Mock对象,若被调用的方法在被委托代表中不存在匹配的方法,在执行时会出现异常。异常示例如下,可参考示例TestSpMockDelegatesToWrong类。

org.mockito.exceptions.base.MockitoException
Methods called on mock must exist in delegated instance.
When calling: public abstract java.lang.String com.adrninistrator.service.TestServiceA1.test1(java.lang.String) on mock: testServiceA1
no such method was found.
Check that the instance passed to delegatesTo() is of the correct type or contains compatible methods
(delegate instance had type: TestApplicationListener)

方法调用被委托的Mock对象,被Stub的方法会执行Stub指定的操作,不会将调用转发给被委托代表。间接调用时Stub也生效。可参考示例TestSpMockDelegatesToStub2类。

在对方法调用被委托的Mock对象进行Stub时,不应使用Mockito.when()方法,会导致被委托代表的方法被执行。应使用Mockito.do…().when()方法进行Stub,避免执行委托代表的方法。可参考示例TestSpMockDelegatesToStub1、TestSpMockDelegatesToStub2类。

1.5. Mockito.when()操作导致真实方法被执行或抛出异常

关于Mockito.when()操作导致真实方法被执行或抛出异常的说明,在对静态方法进行Mock的部分已进行了说明,但存在一些区别。

以下针对非静态公有非void方法进行分析(对于非静态方法,Mock.when().then…()不支持公有非void之外的方法)。

参考“CALLS_REAL_METHODS”( https://static.javadoc.io/org.mockito/mockito-core/latest/org/mockito/Mockito.html#CALLS_REAL_METHODS )。使用when(mock.getSomething()).thenReturn(fakeValue)语法进行Stub会执行真实方法,建议使用doReturn语法。

在对接口执行Mockito.mock()操作时,若默认Answer指定为执行真实方法,当使用Mockito.when().then…()方法执行Stub时,不会执行真实方法。

在对实现类执行Mockito.mock()操作时,若默认Answer指定为执行真实方法,当使用Mockito.when().then…()方法执行Stub时,会执行真实方法。如以下示例中,执行Mockito.when(testPublicNonVoidService1ImplMock.test1())方法时,真实方法会执行:

private TestPublicNonVoidService1 testPublicNonVoidService1ImplMock;

testPublicNonVoidService1ImplMock = Mockito.mock(TestPublicNonVoidService1Impl.class, new CallsRealMethods());

Mockito.when(testPublicNonVoidService1ImplMock.test1(Mockito.anyString())).thenReturn(TestConstants.MOCKED);

以上可参考示例TestSpMockSECallsRealMethods类。

在对接口或实现类执行Mockito.mock()操作时,若默认Answer指定为抛出异常,当使用Mockito.when().then…()方法执行Stub时,会抛出指定的异常。如以下示例中,执行Mockito.when(testPublicNonVoidService1Mock.test1())方法会抛出异常RuntimeException,可参考示例TestSpMockSEThrowsExpt类。

private TestPublicNonVoidService1 testPublicNonVoidService1Mock;

testPublicNonVoidService1Mock = Mockito.mock(TestPublicNonVoidService1.class,
        new ThrowsException(new RuntimeException(TestConstants.MOCKED)));

assertThrows(RuntimeException.class, () ->
        Mockito.when(testPublicNonVoidService1Mock.test1(Mockito.anyString())).thenReturn(TestConstants.MOCKED)
);

在对接口或实现类执行Mockito.mock()操作时,若默认Answer指定为默认的返回空值的Answer,当使用Mockito.when().then…()方法执行Stub时,不会执行真实方法,可参考示例TestSpMockSEReturnsDefaults类。

在对接口或实现类执行Mockito.mock()操作时,若默认Answer指定什么也不做,当使用Mockito.when().then…()方法执行Stub时,不会执行真实方法,可参考示例TestSpMockSEDoesNothing类。

1.5.1. Mockito.when()操作执行真实方法或抛出异常的原因分析

关于Mockito.when()操作执行真实方法或抛出异常的原因,已在前文静态方法部分进行过分析,不再重复。

1.5.2. 避免Stub操作执行真实方法或抛出异常

Mockito.do…().when()方法与Mockito.when().then…()方法功能类似,使用Mockito.do…().when()方法进行Stub时,不会执行真实方法,在有需要时可以使用Mockito.do…().when()方法替代Mockito.when().then…()方法进行Stub。

对非静态方法使用Mockito.do…().when()方法进行Stub的语法为“Mockito.do…().when(Mock对象).非静态方法(Stub参数条件)”,示例如下:

Mockito.doReturn(TestConstants.MOCKED).when(testPublicNonVoidService1ImplMock).test1(Mockito.anyString());

Mockito.when().then…()方法及对应的Mockito.do…().when()方法如下所示:

then…()方法对应的do…()方法
Mockito.when().thenReturn()Mockito.doReturn().when()
Mockito.when().thenThrow()Mockito.doThrow().when()
Mockito.when().thenAnswer()Mockito.doAnswer().when()
Mockito.when().thenCallRealMethod()Mockito.doCallRealMethod().when()

可参考示例TestSpMockSECallsRealMethods、TestSpMockSEThrowsExpt类。


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

相关文章

一本书,一个人

本文转自 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&…

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

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