How to test the WebSocket protocol

Learn how to load test WebSocket with Gatling

WebSocket is a bidirectional communication protocol enabling real-time data exchange between clients and servers. Unlike HTTP, WebSockets maintain a persistent connection for instant, efficient communication. WebSockets are ideal for applications needing real-time updates, like live chat, multiplayer gaming, collaborative editing, in-app notifications, and trading platforms, enhancing user experience and operational efficiency.

Prerequisites

Launch the Server

Let’s create a WebSocket server in JavaScript that we will load test:

const WebSocket = require("ws");

const server = new WebSocket.Server({ port: 8765 });

function send(socket, data, i, n) {
  if (i <= n) {
    setTimeout(function() {
      const message = `${data} ${i}/${n}`;
      socket.send(message);
      console.log(`-> ${message}`);

      send(socket, data, i + 1, n);
    }, Math.floor(Math.random() * 1000));
  }
}

server.on("connection", (socket) => {
  console.log("new client connected");
  socket.send("connected");
  socket.send("connected");

  //socket.on("message", (data) => {
    const message = "hello client"
    //console.log(`message received: ${message}`);
    const n = 5 + Math.floor(Math.random() * 15);
    console.log(`responding with ${n} messages`);
    send(socket, message, 1, n);
  //});

  socket.on("close", () => {
    console.log("client disconnected");
  });
});

console.log("listening on ws://localhost:8765");

This code creates a WebSocket server on port 8765. When someone connects, it sends a random number of messages with a random interval between 0 and 1 second between each message.

Now let’s load test it.

Test Creation

Create the Scenario

In this guide, we create a scenario using Gatling. Our user connects to the server, checks for incoming messages, and prints the rest of the messages:

package websockets;

import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;

import static io.gatling.javaapi.core.CoreDsl.*;
import static io.gatling.javaapi.http.HttpDsl.*;

import io.gatling.http.action.ws.WsInboundMessage;
import io.gatling.javaapi.core.*;
import io.gatling.javaapi.http.*;

public class WebSocketSampleJava extends Simulation {

    HttpProtocolBuilder httpProtocol =
        http.wsBaseUrl("ws://localhost:8765")
            .wsUnmatchedInboundMessageBufferSize(1024);

    ScenarioBuilder scn = scenario("Users")
        .exec(
            ws("Connect").connect("/").await(10).on(
                ws.checkTextMessage("Connect:check")
                    .check(bodyString().is("connected"))
            ),
            during(20).on(
                ws.processUnmatchedMessages((messages, session) -> {
                    var data = messages.stream()
                        .filter(m -> m instanceof WsInboundMessage.Text)
                        .map(m -> ((WsInboundMessage.Text) m).message())
                        .collect(Collectors.joining(", "));
                    System.out.println("messages received last second: " + data);
                    return session;
                }),
                pause(1)
            ),
            ws("Close").close()
        );

The httpProtocol allows Gatling to connect to our WebSocket application. After that, we connect to our application and check if the server returns the correct message. ProcessUnmatchedMessage processes inbound messages that haven’t been matched with a check and have been buffered. In our case, this allows us to display the messages the server sends.

Generate the User

We start with a single user to verify our simulation works correctly. To do that, add the following code to your simulation:

setUp(
    scn.injectOpen(atOnceUsers(1))
).protocols(httpProtocol);

Running the Test

To run the simulation:

  1. Place the simulation file in your project’s test directory.
  2. Run using Maven:
mvn gatling:test

Best Practices

  1. Use secure WebSocket connections (wss://).
  2. Use a buffer or queue to handle incoming messages asynchronously.
  3. Add appropriate pauses between requests to simulate real user behavior.
  4. Include proper error handling.

This basic implementation should get you started with load testing WebSocket applications. If you need a more detailed use case, you can see the list of available commands here.

Edit this page on GitHub