Testing WebSockets


Overview

This page covers WebSocket testing functionality in Artillery provided by the built-in WebSocket engine.

Enabling WebSocket support

To use the WebSocket engine in an Artillery scenario, set the engine attribute of a scenario definition to ws.

scenarios:
- name: My WebSocket test
engine: ws # Enable the Socket.io engine
flow:
- send: "Hello world!"

WebSocket-specific configuration

Setting Subprotocols

WebSocket subprotocols may be configured with config.ws.subprotocols option:

config:
target: "wss://echo.websocket.org"
phases:
- duration: 20
arrivalRate: 10
ws:
# Set WebSocket subprotocols:
subprotocols:
- json
- soap

Specifying Headers

Connection headers may be specified via the config.ws.headers object.

The following example shows how you can set a custom subprotocol called my-protocol via the Sec-WebSocket-Protocol header:

config:
target: "wss://echo.websocket.org"
phases:
- duration: 20
arrivalRate: 10
ws:
# Set a custom WebSocket subprotocol:
headers:
Sec-WebSocket-Protocol: my-protocol

Using a Proxy

The config.ws.proxy object is only available in Artillery v2.

Proxy settings may be specified via the config.ws.proxy object:

config:
target: "wss://echo.websocket.org"
phases:
- duration: 20
arrivalRate: 10
ws:
proxy:
url: "https://proxy:port"

Any additional property will be passed to the underlying HTTP(s) proxy agent.

Other configuration options

You can configure the underlying WebSocket client via additional objects under config.ws.

For example, to change the number of maximum redirects allowed, use the config.ws.maxRedirects object:

config:
target: "wss://echo.websocket.org"
phases:
- duration: 20
arrivalRate: 10
ws:
maxRedirects: 25

For a list of available options, please see WebSocket library docs.

Scenario actions and configuration

The WebSocket engine supports five types of actions: connect, send, think, loop, and function.

Configuring the WebSocket connection with connect:

The connect action is only available in Artillery v2.

connect must be the first step in the flow and it can be a string, a function or an object.

As a string:

config:
target: "wss://echo.websocket.org"
phases:
- duration: 20
arrivalRate: 10
variables:
token:
"mytoken"

scenarios:
- engine: ws
name: Echo a string
flow:
- connect: "{{ target }}/?token={{ token }}"
- send: "Hello from Artillery"

As a function:

config:
target: "wss://echo.websocket.org"
phases:
- duration: 20
arrivalRate: 10
variables:
token:
"mytoken"

scenarios:
- engine: ws
name: Echo a string
flow:
- connect:
function: "connectHandler"
- send: "Hello from Artillery"

The function should have the following signature:

function connectHandler(params, context, next) {
// This is the same as the "string" example above
params.target = "${params.target}/?token=${context.vars.token}";

next();
}

Where:

  • params - An object whose properties will be passed to the underlying WebSocket constructor.
  • context - The virtual user’s context. context.vars is a dictionary containing all defined variables.
  • next - The callback which must be called for the scenario to continue. If a value is passed to it, the execution fails.

As an object:

connect accepts all the parameters of the config.ws section plus a target property:

config:
target: "wss://echo.websocket.org"
phases:
- duration: 20
arrivalRate: 10
variables:
token:
"mytoken"

scenarios:
- engine: ws
name: Echo a string 1
flow:
- connect:
target: "ws://echo.websocket.org/?token={{ token }}"
proxy:
url: "http://proxy1:3000"
- send: "Hello from Artillery"
- engine: ws
name: Echo a string 2
flow:
- connect:
target: "{{ target }}/?token={{ token }}"
proxy:
url: "http://proxy2:3000"
- send: "Hello from Artillery, through a different proxy"

Sending data with send

Use send to send data to the server:

config:
target: "wss://echo.websocket.org"
phases:
- duration: 20
arrivalRate: 10
ws:
maxRedirects: 25

scenarios:
- engine: ws
name: Echo a string
flow:
- send: "Hello from Artillery"

If the argument is an object, it will get converted to a string with JSON.stringify before being sent to the target:

config:
target: "wss://echo.websocket.org"
phases:
- duration: 20
arrivalRate: 10

scenarios:
- engine: ws
name: Echo an object
flow:
# the following will be stringified and sent as '{"x":5,"y":6}'
- send:
x: 5
y: 6

Pausing execution with think

To pause the virtual user for N seconds, use a think action:

config:
target: "wss://echo.websocket.org"
phases:
- duration: 20
arrivalRate: 10

scenarios:
- engine: ws
flow:
- send: "Hello from Artillery"
- think: 5 # Pause for 5 seconds.
- send: "Hello again"

Repeating actions with loop

You can use the loop construct to loop through a number of requests in a scenario.

For example, the following send 100 messages to the target from each virtual user:

config:
target: "wss://echo.websocket.org"
phases:
- duration: 20
arrivalRate: 10

scenarios:
- engine: ws
flow:
- loop:
count: 100
- send: "Hello from Artillery"

See the Loops section of the Testing HTTP guide for a detailed description of the loop action.

Running custom code with function

A function may be run at any point in a scenario with a function action:

config:
target: "wss://echo.websocket.org"
processor: "./my-functions.js"
phases:
- duration: 20
arrivalRate: 10

scenarios:
- engine: ws
flow:
- function: "createTimestampedObject"
- send: "{{ data }}"

JS functions invoked with the function action have full access to the virtual user’s context:

module.exports = { createTimestampedObject };

function createTimestampedObject(userContext, events, done) {
const data = { timestamp: Date.now(), hello: "world" };
// set the "data" variable for the virtual user to use in the subsequent action
userContext.vars.data = data;
return done();
}

Examples

Send a string to a WebSocket server:

config:
target: "wss://echo.websocket.org"
phases:
- duration: 20
arrivalRate: 10

scenarios:
- engine: "ws"
flow:
- send: "Hello from Artillery"

Send an object to a WebSocket server, which will get automatically converted to a JSON string:

config:
target: "wss://echo.websocket.org"
phases:
- duration: 20
arrivalRate: 10

scenarios:
- engine: "ws"
flow:
- send:
id: 1
name: "Artillery

Use the WAMP and XMPP subprotocols:

config:
target: "wss://echo.websocket.org"
phases:
- duration: 20
arrivalRate: 10
ws:
subprotocols:
- wamp
- xmpp

scenarios:
- engine: "ws"
flow:
- send: "Hello from Artillery"

Configure the underlying WebSocket client:

config:
target: "wss://echo.websocket.org"
phases:
- duration: 20
arrivalRate: 10
ws:
protocolVersion: 8
origin: "https://websocket.org"
followRedirects: true
maxPayload: 2048

scenarios:
- engine: "ws"
flow:
- send: "Hello from Artillery"

Connecting Without Sending Any Data

You can use think to emulate clients connecting to the WebSocket server and listening without sending anything themselves:

config:
target: "wss://echo.websocket.org"
phases:
- duration: 20
arrivalRate: 10

scenarios:
- engine: "ws"
flow:
- think: 600 # do nothing for 10m and disconnect

Debugging

If you’re having issues getting your test scenarios to complete successfully, you can print out helpful debugging messages using the DEBUG environment variable.

Print data sent and errors

Set DEBUG=ws when running your tests to view details about the data sent to the WebSocket server and any errors that occurred during the test run.

On macOS and Linux systems, you can temporarily set the DEBUG environment variable by setting its value when running your Artillery test script:

DEBUG=ws artillery run my-script.yaml

For the Windows Command Prompt, you first need to set the DEBUG environment variable using the set command before running the test script:

set DEBUG=ws
artillery run my-script.yaml

If you use PowerShell on Windows, you can set the DEBUG environment variable using $Env:DEBUG before running the test script:

$Env:DEBUG = 'ws'
artillery run my-script.yaml