当前位置: 首页 > 知识库问答 >
问题:

Websocket的单元测试返回Null

谷梁嘉运
2023-03-14

我试图实现一个单元测试的WebsocketSpringbootJUnit

我在关注这个链接,它运行良好,但结果为空,当我用浏览器测试我的websocket时,它会发送所需的结果。

让我展示一下我的测试课

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes =Main.class)
@WebAppConfiguration
public class WebSocketTest {

static final String WEBSOCKET_URI = "http://localhost:8080/api/realtimedata";
static final String WEBSOCKET_TOPIC = "/realtimedata";

BlockingQueue<WebSocketData> blockingQueue;
WebSocketStompClient stompClient;

@BeforeEach
public void setup() {

    blockingQueue = new LinkedBlockingDeque<>();
    stompClient = new WebSocketStompClient(new SockJsClient(
            asList(new WebSocketTransport(new StandardWebSocketClient()))));
}

@Test
public void shouldReceiveAMessageFromTheServer() throws Exception {
    StompSession session = stompClient
            .connect(WEBSOCKET_URI, new StompSessionHandlerAdapter() {})
            .get(1, SECONDS);
    session.subscribe(WEBSOCKET_TOPIC, new DefaultStompFrameHandler());

    String message = "{\"groupId\":\"grp-1\", \"accountId\":\"acc-1\", \"userId\":\"usr-1\"}";
    session.send(WEBSOCKET_TOPIC, message.getBytes()); 
    Assertions.assertEquals("trying to figureout", blockingQueue.poll(1, SECONDS));
}

class DefaultStompFrameHandler implements StompFrameHandler {
    @Override
    public Type getPayloadType(StompHeaders stompHeaders) {
        return WebSocketData.class;
    }

    @Override
    public void handleFrame(StompHeaders stompHeaders, Object o) {
        blockingQueue.offer((WebSocketData) o);
    }
}

}

这是我的< code > WebSocketConfiguration 类

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {

@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
    registry.enableSimpleBroker("/realtimedata");
    registry.setApplicationDestinationPrefixes("/wsconn");
}

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/realtimedata")
            .setAllowedOrigins("*")
            .withSockJS();
}

}

这是我从服务器得到的响应

org.opentest4j.AssertionFailedError: 
Expected :trying to figureout
Actual   :null

更新:

在下面的答案中添加Rieckpil建议的更改并经过进一步的研究后,我的测试类现在看起来像这样

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ServerWebSocketTest {

@LocalServerPort
private Integer port;

static final String WEBSOCKET_TOPIC = "/realtimedata";

BlockingQueue<WebSocketData> blockingQueue;
WebSocketStompClient stompClient;

@BeforeEach
public void setup() {

    blockingQueue = new LinkedBlockingDeque<>();
    stompClient = new WebSocketStompClient(new SockJsClient(
            asList(new WebSocketTransport(new StandardWebSocketClient()))));

    // might be required
    stompClient.setMessageConverter(new MappingJackson2MessageConverter());

}

@Test
public void shouldReceiveAMessageFromTheServer() throws Exception {
    StompSession session = stompClient
            .connect(getWsPath(), **new DefaultStompFrameHandler()** {
            })
            .get(1, TimeUnit.SECONDS);
    session.subscribe(WEBSOCKET_TOPIC, new DefaultStompFrameHandler());

    String message = "{\"groupId\":\"grp-1\", \"accountId\":\"acc-1\", \"userId\":\"usr-1\"}";
    session.send(WEBSOCKET_TOPIC, message.getBytes());
    Assertions.assertEquals("trying to figureout", blockingQueue.poll(1, TimeUnit.SECONDS));
}

class DefaultStompFrameHandler **extends StompSessionHandlerAdapter**{   
    @Override
    public Type getPayloadType(StompHeaders stompHeaders) {
        return WebSocketData.class;
    }

    @Override
    public void handleFrame(StompHeaders stompHeaders, Object o) {
        blockingQueue.offer((WebSocketData) o);
    }

    **@Override
    public void handleException(StompSession session, StompCommand command, StompHeaders headers, byte[] payload, Throwable exception) {
        exception.printStackTrace();
    }**
}
private String getWsPath() {
    return String.format("ws://localhost:%d/location_services/realtimedata", port);
}

}

经过这些更改后,我现在可以将堆栈跟踪打印到日志中了

这是我在这些改变之后得到的异常

 org.springframework.messaging.converter.MessageConversionException: Could not read JSON: Cannot construct instance of `com.convo.locationservices.websocket.WebSocketData` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('eyJncm91cElkIjoiZ3JwLTEiLCAiYWNjb3VudElkIjoiYWNjLTEiLCAidXNlcklkIjoidXNyLTEifQ==')
 at [Source: (byte[])""eyJncm91cElkIjoiZ3JwLTEiLCAiYWNjb3VudElkIjoiYWNjLTEiLCAidXNlcklkIjoidXNyLTEifQ==""; line: 1, column: 1]; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.convo.locationservices.websocket.WebSocketData` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('eyJncm91cElkIjoiZ3JwLTEiLCAiYWNjb3VudElkIjoiYWNjLTEiLCAidXNlcklkIjoidXNyLTEifQ==')
 at [Source: (byte[])""eyJncm91cElkIjoiZ3JwLTEiLCAiYWNjb3VudElkIjoiYWNjLTEiLCAidXNlcklkIjoidXNyLTEifQ==""; line: 1, column: 1], failedMessage=GenericMessage [payload=byte[82], headers={simpMessageType=MESSAGE, stompCommand=MESSAGE, nativeHeaders={destination=[/realtimedata], content-type=[application/json], subscription=[0], message-id=[cc55448f0b4f447d8f6bc7662acd15db-0], content-length=[82]}, simpSubscriptionId=0, contentType=application/json, simpSessionId=2df73ca0-e13b-c479-4591-37e9f7e8d689, simpDestination=/realtimedata}]
    at org.springframework.messaging.converter.MappingJackson2MessageConverter.convertFromInternal(MappingJackson2MessageConverter.java:235)
    at org.springframework.messaging.converter.AbstractMessageConverter.fromMessage(AbstractMessageConverter.java:197)
    at org.springframework.messaging.converter.AbstractMessageConverter.fromMessage(AbstractMessageConverter.java:188)
    at org.springframework.messaging.simp.stomp.DefaultStompSession.invokeHandler(DefaultStompSession.java:477)
    at org.springframework.messaging.simp.stomp.DefaultStompSession.handleMessage(DefaultStompSession.java:429)
    at org.springframework.web.socket.messaging.WebSocketStompClient$WebSocketTcpConnectionHandlerAdapter.handleMessage(WebSocketStompClient.java:340)
    at org.springframework.web.socket.handler.WebSocketHandlerDecorator.handleMessage(WebSocketHandlerDecorator.java:75)
    at org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator.handleMessage(LoggingWebSocketHandlerDecorator.java:56)
    at org.springframework.web.socket.sockjs.client.AbstractClientSockJsSession.handleMessageFrame(AbstractClientSockJsSession.java:294)
    at org.springframework.web.socket.sockjs.client.AbstractClientSockJsSession.handleFrame(AbstractClientSockJsSession.java:230)
    at org.springframework.web.socket.sockjs.client.WebSocketTransport$ClientSockJsWebSocketHandler.handleTextMessage(WebSocketTransport.java:163)
    at org.springframework.web.socket.handler.AbstractWebSocketHandler.handleMessage(AbstractWebSocketHandler.java:43)
    at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.handleTextMessage(StandardWebSocketHandlerAdapter.java:114)
    at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.access$000(StandardWebSocketHandlerAdapter.java:43)
    at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:85)
    at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:82)
    at org.apache.tomcat.websocket.WsFrameBase.sendMessageText(WsFrameBase.java:395)
    at org.apache.tomcat.websocket.WsFrameBase.processDataText(WsFrameBase.java:495)
    at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:294)
    at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:133)
    at org.apache.tomcat.websocket.WsFrameClient.processSocketRead(WsFrameClient.java:95)
    at org.apache.tomcat.websocket.WsFrameClient.resumeProcessing(WsFrameClient.java:209)
    at org.apache.tomcat.websocket.WsFrameClient.access$300(WsFrameClient.java:31)
    at org.apache.tomcat.websocket.WsFrameClient$WsFrameClientCompletionHandler.doResumeProcessing(WsFrameClient.java:186)
    at org.apache.tomcat.websocket.WsFrameClient$WsFrameClientCompletionHandler.completed(WsFrameClient.java:163)
    at org.apache.tomcat.websocket.WsFrameClient$WsFrameClientCompletionHandler.completed(WsFrameClient.java:148)
    at java.base/sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:127)
    at java.base/sun.nio.ch.Invoker$2.run(Invoker.java:219)
    at java.base/sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:112)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.convo.locationservices.websocket.WebSocketData` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('eyJncm91cElkIjoiZ3JwLTEiLCAiYWNjb3VudElkIjoiYWNjLTEiLCAidXNlcklkIjoidXNyLTEifQ==')
 at [Source: (byte[])""eyJncm91cElkIjoiZ3JwLTEiLCAiYWNjb3VudElkIjoiYWNjLTEiLCAidXNlcklkIjoidXNyLTEifQ==""; line: 1, column: 1]
    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
    at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1432)
    at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1062)
    at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:371)
    at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:323)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1373)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:171)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4218)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3309)
    at org.springframework.messaging.converter.MappingJackson2MessageConverter.convertFromInternal(MappingJackson2MessageConverter.java:219)
    ... 31 more
            

org.opentest4j.AssertionFailedError: 
Expected :trying to figureout
Actual   :null
 

有人能告诉我我做错了什么吗?

谢谢。

共有2个答案

公西凯捷
2023-03-14

我遇到了同样的问题。在我的情况下,我忘记添加默认构造函数和所有参数构造函数

请检查您的WebSocketData类以添加构造函数

叶浩荡
2023-03-14

您应该将集成测试配置为使用启动的Spring上下文,并且不要硬编码8080

此外,请确保您为 WebSocketStomp 客户端配置了适合您的用例的消息转换器。默认值为简单消息转换器。当您发送字节 [] 时,字节数组消息转换器应该适合。

您的测试可以重构为以下内容:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class Test {

  @LocalServerPort
  private Integer port;
  
  static final String WEBSOCKET_TOPIC = "/realtimedata";

  BlockingQueue<WebSocketData> blockingQueue;
  WebSocketStompClient stompClient;

  @BeforeEach
  public void setup() {

    blockingQueue = new LinkedBlockingDeque<>();
    stompClient = new WebSocketStompClient(new SockJsClient(
      asList(new WebSocketTransport(new StandardWebSocketClient()))));

    webSocketStompClient.setMessageConverter(new ByteArrayMessageConverter()); 

  }

  @Test
  public void shouldReceiveAMessageFromTheServer() throws Exception {
    StompSession session = stompClient
      .connect(getWsPath(), new StompSessionHandlerAdapter() {
      })
      .get(1, SECONDS);
    session.subscribe(WEBSOCKET_TOPIC, new DefaultStompFrameHandler());

    String message = "{\"groupId\":\"grp-1\", \"accountId\":\"acc-1\", \"userId\":\"usr-1\"}";
    session.send(WEBSOCKET_TOPIC, message.getBytes());
    Assertions.assertEquals("trying to figureout", blockingQueue.poll(1, SECONDS));
  }

  class DefaultStompFrameHandler implements StompFrameHandler {
    @Override
    public Type getPayloadType(StompHeaders stompHeaders) {
      return WebSocketData.class;
    }

    @Override
    public void handleFrame(StompHeaders stompHeaders, Object o) {
      blockingQueue.offer((WebSocketData) o);
    }
  }
  
  private String getWsPath() {
    return String.format("ws://localhost:%d/api/realtimedata", port);
  }

}

这个测试将启动整个Spring上下文,即随机端口上的嵌入式servlet容器,并尝试连接到您的endpoint。

 类似资料:
  • 我有一个spring boot应用程序,它有一个endpoint测试配置类和一个单元测试来测试我的http客户端。我正在尝试从应用程序中获取服务器地址和端口。位于我的src/测试中的属性。(所有类都在我的src/测试中。) 这是我的配置类代码: 我对我的中的值进行了注释。属性,然后创建一个方法,我用bean实例化该方法以返回我的服务器地址字符串。 然后,我在HttpClientTest类中使用该字

  • 我使用textutils.join连接一个字符串列表,但它返回null。我是在一个简单的单元测试中这样做的。 下面是有问题的代码:

  • 测试方法: 测试用例:

  • 我有一个用@service注释的类(HttpHandler)。我正在为这个服务类编写单元测试用例。我在测试类中使用@Autowired注释来获取服务的对象。但是,当我运行单元测试时,它返回NullPointerException。有人能帮帮我吗? 下面是服务类的代码:

  • 问题内容: 我编写了一个测试UsersController的单元测试。UsersControllerTest.findUser工作正常,但不能正常运行UsersControllerTest.insertGetModifyDelete。 在测试日志中,我可以看到POST请求与UsersController的任何方法都不匹配,但是我不明白为什么。您能帮我这个吗? 这是我其余的Java类: 我有2种方法

  • 问题内容: 根据Michal Charemza帖子编辑。 我有一个代表angularui模态对话框的服务: 如果用户单击对话框中的“确定”,则在调用delete方法时将执行。 问题是我无法对此进行单元测试。 这是我的考验。我已经正确注入了q服务,但是我不确定应该从间谍那里返回什么… 但是我正在接受。这意味着…部分没有执行。我想念什么? 问题答案: 要模拟返回承诺的函数,它还需要返回承诺,然后需要将