本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford

通过单元测试,我们也可以了解下一般我们实现 spring cloud 自定义的基础组件,怎么去单元测试。

这里的单元测试主要测试三个场景:

  1. 只返回同一个 zone 下的实例,其他 zone 的不会返回
  2. 对于多个请求,每个请求返回的与上次的实例不同。
  3. 对于多线程的每个请求,如果重试,返回的都是不同的实例

同时,我们也需要针对同步和异步两个配置,分别进行测试,同步和异步两种配置测试逻辑是一样的,只是测试的 Bean 不一样

  • 同步环境是 DiscoveryClient,异步环境是 ReactiveDiscoveryClient
  • 同步环境负载均衡器是 LoadBalancer,异步环境负载均衡器是 ReactiveLoadBalancer

同步测试代码请参考LoadBalancerTest.java异步测试代码请参考LoadBalancerTest.java

我们这里使用同步测试代码作为例子展示:

//SpringExtension也包含了MockitoJUnitRunner,所以 @Mock 等注解也生效了
@ExtendWith(SpringExtension.class)
@SpringBootTest(properties = {LoadBalancerEurekaAutoConfiguration.LOADBALANCER_ZONE + "=zone1"})
public class LoadBalancerTest { @EnableAutoConfiguration
@Configuration
public static class App {
@Bean
public DiscoveryClient myDiscoveryClient() {
ServiceInstance zone1Instance1 = Mockito.spy(ServiceInstance.class);
ServiceInstance zone1Instance2 = Mockito.spy(ServiceInstance.class);
ServiceInstance zone2Instance3 = Mockito.spy(ServiceInstance.class);
Map<String, String> zone1 = Map.ofEntries(
Map.entry("zone", "zone1")
);
Map<String, String> zone2 = Map.ofEntries(
Map.entry("zone", "zone2")
);
when(zone1Instance1.getMetadata()).thenReturn(zone1);
when(zone1Instance1.getInstanceId()).thenReturn("instance1");
when(zone1Instance2.getMetadata()).thenReturn(zone1);
when(zone1Instance2.getInstanceId()).thenReturn("instance2");
when(zone2Instance3.getMetadata()).thenReturn(zone2);
when(zone2Instance3.getInstanceId()).thenReturn("instance3");
DiscoveryClient spy = Mockito.spy(DiscoveryClient.class);
Mockito.when(spy.getInstances("testService"))
.thenReturn(List.of(zone1Instance1, zone1Instance2, zone2Instance3));
return spy;
}
} @SpyBean
private LoadBalancerClientFactory loadBalancerClientFactory;
@SpyBean
private Tracer tracer; /**
* 只返回同一个 zone 下的实例
*/
@Test
public void testFilteredByZone() {
ReactiveLoadBalancer<ServiceInstance> testService =
loadBalancerClientFactory.getInstance("testService");
for (int i = 0; i < 100; i++) {
ServiceInstance server = Mono.from(testService.choose()).block().getServer();
//必须处于和当前实例同一个zone下
Assertions.assertEquals(server.getMetadata().get("zone"), "zone1");
}
} /**
* 返回不同的实例
*/
@Test
public void testReturnNext() {
ReactiveLoadBalancer<ServiceInstance> testService =
loadBalancerClientFactory.getInstance("testService");
Span span = tracer.nextSpan();
for (int i = 0; i < 100; i++) {
try (Tracer.SpanInScope cleared = tracer.withSpanInScope(span)) {
ServiceInstance server1 = Mono.from(testService.choose()).block().getServer();
ServiceInstance server2 = Mono.from(testService.choose()).block().getServer();
//每次选择的是不同实例
Assertions.assertNotEquals(server1.getInstanceId(), server2.getInstanceId());
}
}
} /**
* 跨线程,默认情况下是可能返回同一实例的,在我们的实现下,保持
* span 则会返回下一个实例,这样保证多线程环境同一个 request 重试会返回下一实例
*
* @throws Exception
*/
@Test
public void testSameSpanReturnNext() throws Exception {
Span span = tracer.nextSpan();
for (int i = 0; i < 100; i++) {
try (Tracer.SpanInScope cleared = tracer.withSpanInScope(span)) {
ReactiveLoadBalancer<ServiceInstance> testService =
loadBalancerClientFactory.getInstance("testService");
ServiceInstance server1 = Mono.from(testService.choose()).block().getServer();
AtomicReference<ServiceInstance> server2 = new AtomicReference<>();
Thread thread = new Thread(() -> {
try (Tracer.SpanInScope cleared2 = tracer.withSpanInScope(span)) {
server2.set(Mono.from(testService.choose()).block().getServer());
}
});
thread.start();
thread.join();
System.out.println(i);
Assertions.assertNotEquals(server1.getInstanceId(), server2.get().getInstanceId());
}
}
}
}

运行测试,测试通过。

我们这一节使用单元测试验证我们要实现的这些功能是否有效。下一节,我们将开始分析同步环境下的 Http 客户端,Open-Feign Client。

微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer

最新文章

  1. 【Linux程序设计】之Linux库函数的使用,多文件程序开发,静态与共享函数
  2. 59.DDR3_IP核文件设置
  3. js Touch事件(向左滑动,后退)
  4. sqlserver授予用户查看执行计划的权限
  5. 3 linux、windows环境---路径分隔符不同导致的问题
  6. Outlook 客户端无法通过 MAPI over HTTP 连接
  7. 初探机器学习之使用百度EasyDL定制化模型
  8. 洛谷 P5304 [GXOI/GZOI2019]旅行者(最短路)
  9. 【ASP.NET Core快速入门】(十一)应用Jwtbearer Authentication、生成jwt token
  10. python中关于turtle库的学习笔记
  11. 使用DatagramSocket和DatagramPacket进行简单的通信
  12. 谈谈一些有趣的CSS题目(十六)-- 奇妙的 background-clip: text
  13. modelsim如何使用tcl脚本来写编译文件
  14. Git-git rebase详解
  15. 初学者在Mysql8.0连接时的几个常见基本问题
  16. 初探diskstats
  17. 初识Spring——Spring核心容器
  18. 高可用数据采集平台(如何玩转3门语言php+.net+aauto)
  19. 把CString转化为char*
  20. 怎样查看lInux系统中的所有运行进程

热门文章

  1. Leetcode1.两数之和——简洁易懂
  2. golang拾遗:内置函数len的小知识
  3. jvm源码解读--05 常量池 常量项的解析JVM_CONSTANT_Utf8
  4. 正则:支持6-20位数字、字母和特殊字符(仅限!@#$%^&amp;*())
  5. 解决 OnDropFiles 可能无响应的问题【转】
  6. C++//递增运算符重载
  7. 2020年Android开发年终总结之如何挤进一线大厂?
  8. 了解CSS in JS(JSS)以及在React项目中配置并使用JSS
  9. STP生成树的一些笔记
  10. HTTP缓存——协商缓存(缓存验证)