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

Spring MVC环境下如何使用JUnit4编写javax.websocket单元测试

问题解答

可以沿用「Mock核心依赖+无需启动完整外部容器」的测试思路,不过你使用的是JSR 356标准的原生@ServerEndpoint注解实现WebSocket,没有用到Spring封装的WebSocket模块,所以无法直接复用MockMvc工具,有两种落地方式可选:


方案一:纯单元测试(无需启动容器,执行速度最快)

直接Mock Session等WebSocket容器提供的对象,调用端点的生命周期方法验证业务逻辑即可,不需要启动任何服务。

依赖引入

如果项目没有引入Mockito,先添加测试依赖:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>2.28.2</version>
    <scope>test</scope>
</dependency>

测试代码示例

package com.ws.socket;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import javax.websocket.RemoteEndpoint;
import javax.websocket.Session;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class SocketUnitTest {
    // Mock容器提供的Session对象
    @Mock
    private Session session;
    // Mock消息发送工具对象
    @Mock
    private RemoteEndpoint.Basic basicRemote;

    @Test
    public void testOnMessageLogic() throws Exception {
        // 构造测试参数
        String testSessionId = "test-socket-001";
        String testMsg = "hello websocket";

        // 绑定mock方法的返回值
        when(session.getId()).thenReturn(testSessionId);
        when(session.getBasicRemote()).thenReturn(basicRemote);

        // 直接调用WebSocket端点的生命周期方法
        Socket socket = new Socket();
        socket.onOpen(session);
        socket.onMessage(session, testMsg);

        // 验证逻辑是否符合预期:是否给客户端返回了指定格式的消息
        verify(basicRemote, times(1)).sendText(" already receive msg about your say " + testMsg);
    }
}

该方案适合验证核心业务逻辑,执行速度最快,优先选择该方案测试功能。


方案二:集成测试(启动嵌入式Tomcat,模拟真实连接全流程)

如果需要覆盖连接建立、握手、消息收发的完整流程,可以在测试中启动嵌入式Tomcat,注册WebSocket端点后用客户端发起真实请求测试,和你现有的Spring测试逻辑可以对齐。

依赖引入

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-core</artifactId>
    <version>8.5.87</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-websocket</artifactId>
    <version>8.5.87</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>javax.websocket</groupId>
    <artifactId>javax.websocket-client-api</artifactId>
    <version>1.1</version>
    <scope>test</scope>
</dependency>

测试代码示例

package com.ws.socket;

import com.ws.config.ServletConfig;
import com.ws.config.SpringConfig;
import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
import org.apache.tomcat.websocket.server.WsContextListener;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import javax.websocket.*;
import java.net.URI;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;

@WebAppConfiguration
@ContextConfiguration(classes = {ServletConfig.class, SpringConfig.class})
@RunWith(SpringJUnit4ClassRunner.class)
public class SocketIntegrationTest {
    private Tomcat tomcat;
    private final int port = 8082;
    private final String wsEndpointUrl = "ws://localhost:" + port + "/MyEndpoint";

    @Before
    public void startTomcat() throws Exception {
        // 启动嵌入式Tomcat
        tomcat = new Tomcat();
        tomcat.setPort(port);
        Context context = tomcat.addContext("", System.getProperty("java.io.tmpdir"));
        // 注册WebSocket支持
        context.addApplicationListener(WsContextListener.class.getName());
        // 注册自定义WebSocket端点
        ServerEndpointConfig endpointConfig = ServerEndpointConfig.Builder.create(Socket.class, "/MyEndpoint")
                .configurator(new ServerEndpointConfig.Configurator() {})
                .build();
        context.getServletContext().setAttribute("javax.websocket.server.ServerContainer", endpointConfig);
        tomcat.start();
    }

    @Test
    public void testFullWebSocketFlow() throws Exception {
        CountDownLatch latch = new CountDownLatch(1);
        String testMsg = "test integration msg";
        String[] receivedMsg = new String[1];

        // 构造WebSocket客户端发起连接
        ClientEndpointConfig clientConfig = ClientEndpointConfig.Builder.create().build();
        WebSocketContainer container = ContainerProvider.getWebSocketContainer();
        Session session = container.connectToServer(new Endpoint() {
            @Override
            public void onOpen(Session session, EndpointConfig config) {
                session.addMessageHandler(String.class, message -> {
                    receivedMsg[0] = message;
                    latch.countDown();
                });
                try {
                    session.getBasicRemote().sendText(testMsg);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, clientConfig, URI.create(wsEndpointUrl));

        // 等待接收返回结果,超时时间3秒
        boolean received = latch.await(3, TimeUnit.SECONDS);
        session.close();

        assertEquals(true, received);
        assertEquals(" already receive msg about your say " + testMsg, receivedMsg[0]);
    }

    @After
    public void stopTomcat() throws Exception {
        if (tomcat != null) {
            tomcat.stop();
            tomcat.destroy();
        }
    }
}

该方案适合测试协议层、容器适配相关的逻辑,执行速度稍慢。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 17:36:03