html5手机网站制作教程,番禺网站开发多少钱,百度快照比网站上线时间早,网站安全风险提示单问题描述
在使用MockBean 和 SpyBean 注解的时候#xff0c;需要注意#xff1a;MockBean 会完全 mock 掉这个 bean#xff0c;也就是说#xff0c;假如你指定了 在调用方法 A 时按 mock 期望返回#xff0c;而没有指定调用方法 B 时返回什么#xff0c;那么你在调用方法…问题描述
在使用MockBean 和 SpyBean 注解的时候需要注意MockBean 会完全 mock 掉这个 bean也就是说假如你指定了 在调用方法 A 时按 mock 期望返回而没有指定调用方法 B 时返回什么那么你在调用方法 B 时方法 B 会直接返回 Null换句话说当你使用 MockBean 的时候那个 bean 除了按你给出的 case 返回之外其余情况都返回 null。而SpyBean 不一样SpyBean 支持“非预设时进行真实调用”即假如你指定了 在调用方法 A 时按 mock 期望返回而没有指定调用方法 B 时返回什么那么你在调用方法 B 时方法 B 会进行真实的调用这是 SpyBean 比 MockBean 要丰富的功能。但是SpyBean 只能用在真实的 bean 身上而用在接口上时则会直接报错错误内容是接口不能够被实例化。因此在对外部接口进行 mock 时往往只能使用MockBean。
但是MockBean 在用在接口上时有时会出现一种 case 是MockBean mock 的 bean 并不能在长链路调用时一直生效。比如现有的调用链路是 A-B-C-DD 是外部接口现在我使用 MockBean 标记 D期望 D 按照我 mock 的行为来返回但是会出现一个问题在单测中我的入口是 A结果走到 D 时仍旧会发起真实调用而如果你再写一个单测直接测调用 D 时的表现却发现 D 的表现符合当初的 mock 预期这是为什么呢
举例说明
RunWith(SpringJUnit4ClassRunner.class)
SpringBootTest(classes BgMoriaApplication.class)
ActiveProfiles(test)
WebAppConfiguration
Slf4j
DirtiesContext(classMode DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
ComponentScan(excludeFilters ComponentScan.Filter(type FilterType.ASSIGNABLE_TYPE,classes {TigerConcurrentConsumer.class, TigerMessageProducer.class}
))
public abstract class BaseApiTest {MockBeanprivate RegulatedUserAddressSnapshotQueryForSPService mockServicepublic void mockRegulatedQueryAddressEmptyResponse() {Mockito.when(mockService.queryAddressInfoForPaymentScene(any())).thenReturn(buildEmptyBaseResponse(QueryAddressSnapshotResponse.class));}
}
在上面的单测基础类中我定义了要 mock 的接口为mockService.queryAddressInfoForPaymentScene并期望他返回一个空对象。
而我实际的调用链路为 1.addressSnapshotService.queryHolderNameAddress -2.raphaelIntegration.queryAddressSnapshot -3. regulatedUserAddressSnapshotQueryForSPService.queryAddressInfoForPaymentScene
我的单测为
Slf4j
public class SorosRiskSpiTest extends BaseApiTest{Testpublic void testQueryRegulatedHolderNameAddressAndMobileWithEmptyResponse_NonRegulatedNotNullResponse() {mockRegulatedQueryAddressEmptyResponse();mockRegulatedQueryAddressMobileEmptyResponse();Long uid 27953383161566L;String addressSnapshotId 228100002134397257;String regionId 128;UserAddressSnapshotVO addressSnapshotVO addressSnapshotService.queryHolderNameAddress(uid, addressSnapshotId, regionId);Assert.notNull(addressSnapshotVO);AddressSnapshotMobileVO userAddressSnapshotMobileVO addressSnapshotService.queryAddressSnapshotMobile(uid, addressSnapshotId, regionId);Assert.notNull(userAddressSnapshotMobileVO);}
}
运行单测会发现还是真实地调用了regulatedUserAddressSnapshotQueryForSPService.queryAddressInfoForPaymentScene。 可能的一个原因是Bean 依赖注入的顺序 和 Spring 上下文刷新机制有关也可能跟多层代理机制有关。RaphaelIntegration 在 Spring 容器初始化时已经注入了原始的 RegulatedUserAddressSnapshotQueryForSPService Bean即使后续通过 MockBean 创建了 Mock 对象RaphaelIntegration 内部的引用仍指向原始 Bean而对于 dubbo 接口(而 Dubbo 接口的实现原理就是动态代理)Spring 可能为其创建了动态代理导致 MockBean 无法直接覆盖。 解决办法
怎么才能让这个 mock 的 bean 在长链路中一直生效呢用下面的写法
RunWith(SpringJUnit4ClassRunner.class)
SpringBootTest(classes BgMoriaApplication.class)
ActiveProfiles(test)
WebAppConfiguration
Slf4j
DirtiesContext(classMode DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
ComponentScan(excludeFilters ComponentScan.Filter(type FilterType.ASSIGNABLE_TYPE,classes {TigerConcurrentConsumer.class, TigerMessageProducer.class}
))
public abstract class BaseApiTest {Autowiredprivate ApplicationContext ctx;MockBeanprivate RegulatedUserAddressSnapshotQueryForSPService mockServicepublic void mockRegulatedQueryAddressEmptyResponse() {Mockito.when(mockService.queryAddressInfoForPaymentScene(any())).thenReturn(buildEmptyBaseResponse(QueryAddressSnapshotResponse.class));ReflectionTestUtils.setField(ctx.getBean(RaphaelIntegration.class),regulatedUserAddressSnapshotQueryForSPService,mockService);}
}
上面的写法实际上就是用 mock 的 bean 强制覆盖上下文中的 bean这样你在调用RaphaelIntegration 时里面的regulatedUserAddressSnapshotQueryForSPService 就是 mock 的 bean。 进阶
那如果对于同一个接口 service 下面的多个接口我想让 A 按自己 mock 的结果返回而让 B 进行真实调用这又如何实现呢
RunWith(SpringJUnit4ClassRunner.class)
SpringBootTest(classes BgMoriaApplication.class)
ActiveProfiles(test)
WebAppConfiguration
Slf4j
DirtiesContext(classMode DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
ComponentScan(excludeFilters ComponentScan.Filter(type FilterType.ASSIGNABLE_TYPE,classes {TigerConcurrentConsumer.class, TigerMessageProducer.class}
))
public abstract class BaseApiTest {ResourceQualifier(regulatedUserAddressSnapshotQueryForSPService)private RegulatedUserAddressSnapshotQueryForSPService realService;private RegulatedUserAddressSnapshotQueryForSPService mockService Mockito.mock(RegulatedUserAddressSnapshotQueryForSPService.class);;Autowiredprivate ApplicationContext ctx;public void mockRegulatedQueryAddressMobileNullResponse() {Mockito.when(mockService.queryAddressRealMobileForPaymentScene(any())).thenReturn(null);ReflectionTestUtils.setField(ctx.getBean(RaphaelIntegration.class),regulatedUserAddressSnapshotQueryForSPService,mockService);}public void mockRegulatedQueryAddressRealResponse() {Mockito.when(mockService.queryAddressInfoForPaymentScene(any())).thenAnswer(inv - realService.queryAddressInfoForPaymentScene(inv.getArgument(0)));ReflectionTestUtils.setField(ctx.getBean(RaphaelIntegration.class),regulatedUserAddressSnapshotQueryForSPService,mockService);}
}
如上所示通过 thenAnswer 的方式来动态路由 mock 的路线从而实现同一个 service 下的不同接口有不同的表现。