You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不使用MockMvc时结合SpringJUnit4ClassRunner进行Jetty WebSocket测试?

我之前也碰到过这个Jetty+Spring WebSocket集成测试的棘手问题,核心原因确实是Spring的MockServletContext没实现Jetty要求的Context接口,导致依赖MockMvc的测试方式完全行不通。不过不用慌,我们可以直接启动真实的Jetty容器来配合SpringJUnit4ClassRunner做测试,具体方案如下:

解决方案:启动真实Jetty容器进行集成测试

1. 搭建测试类基础结构

你的测试类依然可以保留SpringJUnit4ClassRunner和@ContextConfiguration来加载Spring上下文,但必须去掉@WebAppConfiguration——这个注解会触发Spring创建MockServletContext,而这正是导致兼容性问题的根源。

接下来,我们要在测试类里手动初始化、启动Jetty容器,并把Spring的DispatcherServlet和WebSocket组件注册进去。示例代码如下:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {ApplicationConfig.class, WebSocketConfig.class})
public class JettyWebSocketIntegrationTest {

    private Server jettyServer;
    private final String baseUrl = "http://localhost:8080";

    @Autowired
    private ApplicationContext applicationContext;

    @Before
    public void startJetty() throws Exception {
        // 初始化Jetty服务器,指定端口
        jettyServer = new Server(8080);

        // 创建Servlet上下文处理器
        ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
        contextHandler.setContextPath("/");
        jettyServer.setHandler(contextHandler);

        // 注册Spring的DispatcherServlet,关联已加载的Spring上下文
        ServletHolder dispatcherHolder = new ServletHolder(new DispatcherServlet(applicationContext));
        contextHandler.addServlet(dispatcherHolder, "/*");

        // 注册Jetty WebSocket处理器,关联你的WebSocket端点类
        WebSocketHandler wsHandler = new WebSocketHandler() {
            @Override
            public void configure(WebSocketServletFactory factory) {
                // 替换成你自己的WebSocket端点类(比如带@ServerEndpoint注解的类)
                factory.register(YourWebSocketEndpoint.class);
            }
        };
        // 指定WebSocket的访问路径
        contextHandler.addHandler(wsHandler, "/ws/**");

        // 启动Jetty
        jettyServer.start();
    }

    @After
    public void stopJetty() throws Exception {
        // 测试结束后停止并销毁Jetty,避免端口占用
        if (jettyServer != null) {
            jettyServer.stop();
            jettyServer.destroy();
        }
    }

    // 测试方法示例:用真实WebSocket客户端验证功能
    @Test
    public void testWebSocketMessageExchange() throws Exception {
        // 初始化Jetty的WebSocket客户端
        WebSocketClient client = new WebSocketClient();
        client.start();

        // 创建自定义的WebSocket监听器,用于接收服务端响应
        TestWebSocketListener listener = new TestWebSocketListener();
        // 连接到服务端的WebSocket端点
        Session session = client.connect(listener, new URI(baseUrl + "/ws/test-endpoint")).get();

        // 发送测试消息
        session.getRemote().sendString("Hello Jetty WebSocket!");

        // 等待监听器收到响应,做断言验证(这里可以用CountDownLatch来同步)
        listener.getLatch().await(5, TimeUnit.SECONDS);
        assertEquals("Expected response not received", "Received: Hello Jetty WebSocket!", listener.getReceivedMessage());

        // 关闭客户端
        client.stop();
    }

    // 自定义测试用WebSocket监听器
    private static class TestWebSocketListener extends WebSocketAdapter {
        private final CountDownLatch latch = new CountDownLatch(1);
        private String receivedMessage;

        @Override
        public void onWebSocketText(String message) {
            receivedMessage = message;
            latch.countDown();
        }

        public CountDownLatch getLatch() {
            return latch;
        }

        public String getReceivedMessage() {
            return receivedMessage;
        }
    }
}

2. 适配Spring WebSocket消息代理(如STOMP)

如果你的项目用了Spring的WebSocket消息代理(比如STOMP协议),不需要手动注册Jetty的WebSocketHandler,而是要在Spring配置中指定Jetty专属的升级策略,让Spring和Jetty的WebSocket机制兼容:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
        // 配置WebSocket传输参数
        registration.setMessageSizeLimit(1024 * 1024);
        registration.setSendTimeLimit(20000);
        // 指定Jetty的WebSocket升级策略
        registration.setUpgradeStrategy(new WebSocketJettyRequestUpgradeStrategy());
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        // 注册STOMP端点,支持SockJS降级
        registry.addEndpoint("/stomp-ws").withSockJS();
    }

    // 其他消息代理配置...
}

这种情况下,测试类里只需要注册DispatcherServlet即可,Spring会自动处理WebSocket的升级请求,你可以用STOMP客户端(比如Spring的StompClient)来进行测试。

3. 核心注意事项

  • 必须移除@WebAppConfiguration:这个注解会强制Spring创建MockServletContext,而它不兼容Jetty的Context接口,是问题的核心诱因。
  • 手动管理Jetty生命周期:用@Before和@After注解分别启动、销毁Jetty,确保每个测试用例都有独立干净的容器环境。
  • 使用真实客户端测试:不要依赖MockMvc,而是用真实的WebSocket/STOMP客户端来模拟实际场景,这样才能覆盖WebSocket的全流程功能。

通过这种方式,你既可以保留SpringJUnit4ClassRunner来便捷加载Spring上下文,又能绕过MockMvc的兼容性问题,顺利完成Jetty上的WebSocket集成测试。

内容的提问来源于stack exchange,提问作者jmc42

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 03:53:32