当前位置: 首页 > news >正文

springboot项目之websocket的坑:spring整合websocket后进行单元测试后报错的解决方案

前排提醒:还是博主菜,见识短浅,没遇到过这个问题。。。

起因

前段时间学习websocket和sse,写demo用了spring框架。后来又写了新的spring单元测试类demo去测试,结果启动后报错,报错信息提示websocket的相关错误,大概意思时由于测试环境缺少WebSocket相关配置或依赖所导致。

报错信息


java.lang.IllegalStateException: Failed to load ApplicationContextat org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:98)at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124)at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190)at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132)at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:248)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)at org.junit.runners.ParentRunner.run(ParentRunner.java:413)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)at org.junit.runner.JUnitCore.run(JUnitCore.java:137)at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'serverEndpointExecutor' defined in class path resource [com/realpractice/demo/config/WebSocketConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: javax.websocket.server.ServerContainer not availableat org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1804)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955)at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:745)at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:420)at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:144)at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:141)at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:90)... 27 more
Caused by: java.lang.IllegalStateException: javax.websocket.server.ServerContainer not availableat org.springframework.util.Assert.state(Assert.java:76)at org.springframework.web.socket.server.standard.ServerEndpointExporter.afterPropertiesSet(ServerEndpointExporter.java:107)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800)... 42 more

这是博主用的很简单的一个测试类:

import xxx.xxx.xxx.config.RedisConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;import java.io.Serializable;@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class)
@ContextConfiguration(classes = RedisConfig.class) // 启用自定义配置
public class RedisTest {@Autowiredprivate RedisTemplate<String, String> redisTemplate;@Testpublic void test() {redisTemplate.opsForValue().set("test", "test");System.out.println(redisTemplate.opsForValue().get("test"));}
}

启动后报错:

刚开始有点懵,我都没测你websocket,你给我报个websocket的错。

学习一波。。。

根本原因是由于测试环境下没有websocket的环境,所以测试的时候需要注释掉它。并且测试中不选择让websocket加载有很多种方式,这个我们稍后再说。先来看看spring本身,既然报了websocket的错,说明spring本身去加载到了项目config目录下所有使用@Configuration注解的配置类。那么抛出我们第一个问题:为什么非 WebSocket 的测试类会加载 WebSocket 配置?

问题

为什么测试类会加载 WebSocket 配置?

这就不得不提到springboot的自动加载机制:

@SpringBootTest 会加载整个应用的上下文(包括所有 @Component, @Configuration, @Service 等)。

只要你的 WebSocketConfig 类被 @Configuration 或 @EnableWebSocket 标记,它就会被扫描并初始化。


那有的朋友又要问了,那我知道了springboot的自动加载机制,正常启动项目websocket也没错,为什么一使用测试就报错呢?不是说@SpringBootTest会加载整个应用的上下文吗?

嗯,博主看了下,原因好像是底层的依赖冲突,导致spring加载了错误的实现类。【详细原因的博主自己还没真正的测试过,就不妄下结论了。感兴趣的朋友直接浏览器搜索:“javax.websocket.server.ServerContainer not available”,跟着打波断点走下流程肯定能更明白整个逻辑】


多说一些关于sprngboot的相关知识把:

spring的组件扫描规则说白了就是"约定优于配置"的具体体现,即spring默认去加载所有可能的组件,减少显式配置。

还有一点是"上下文完整性":即spring确保测试环境和生产环境一致,避免因部分组件未加载导致测试结果不准确。

但也会带来一些副作用:测试启动变慢 和 依赖耦合,导致最终加载了不必要的组件,可能因它的初始化失败导致整个测试类失败。

解决

好好好,说了这么多怎么解决这个问题呢?

首先简单粗暴的办法就是在测试时把websocket相关代码全部注释掉,正常启动时再取消注释就可以了。(下下策)

我们可以把websocket的config写成根据不同配置选择是否加载,让其判断当前是否是测试环境,如果是则不加载websocket相关业务代码:

(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

通过注解参数 完全初始化 WebSocket 的基础设施,包括 ServerContainer。

明确指定了主配置类,并启动一个真实的嵌入式 web 服务器(而不仅仅是 mock 环境),并随机选择一个可用端口。

。。。总之是可以运行的。。。

内容还有待补充,有问题的朋友可以评论一起交流一下。文章如有错误劳驾读者朋友们指正,博主诚恳接受学习。谢谢大家。🌠

http://www.xdnf.cn/news/193411.html

相关文章:

  • Qt6.8.2中WebAssembly沙盒环境中预加载文件
  • Cursor
  • 可视化图解算法:合并二叉树
  • JVM 生产环境问题定位与解决实战(八):实战篇——正则表达式回溯引发的CPU 100%
  • C++20 小语法
  • 【KWDB 创作者计划】_KWDB产品技术解读
  • 【线性规划】对偶问题的实际意义与重要性质 学习笔记
  • 鼠标获取坐标 vs 相机获取坐标
  • SpringBoot应用原生或docker镜像容器集成Skywalking
  • 数据要素与居民就业的深层联结 数字化转型下的劳动力市场变革
  • 项目上线流程梳理(Linux宝塔面板)
  • 基于Springboot + vue + 爬虫实现的高考志愿智能推荐系统
  • Web基础与HTTP协议
  • 第二章、Isaaclab强化学习包装器(1)
  • 研究:大模型输出一致性:确定性与随机性的场景化平衡
  • 【Android】SettingsPreferenceService
  • (002)Excel 使用图表,统计
  • conda和bash主环境的清理
  • 【优秀三方库研读】【性能优化点滴】odygrd/quill 解决伪共享
  • AcWing 885:求组合数 I ← 杨辉三角
  • vs2022解决 此项目需要MFC库。从visual studio安装程序(单个组件选项卡)为正在使用的任何工具和体系结构安装他们问题
  • JQ6500语音模块详解(STM32)
  • C++ 之 【模拟实现 list(节点、迭代器、常见接口)】(将三个模板放在同一个命名空间就实现 list 啦)
  • 电子电器架构 -- 汽车零部件DV试验与PV试验的定义及关键差异
  • [ 问题解决 ] sqlite3.ProgrammingError: SQLite objects created in a thread can ...
  • mybatis的xml ${item}总是更新失败
  • npm init、换源问题踩坑
  • 【Python数据驱动决策】数据分析与可视化全流程实战指南
  • 论文导读 - 基于边缘计算、集成学习与传感器集群的便携式电子鼻系统
  • Vue基础(7)_计算属性