ToolsUnit Testing

Unit Testing Your Server

The @colyseus/testing package provides utility methods for testing your Colyseus application. It uses the JavaScript SDK to simulate client connections and send messages to the server.

Use it with your favorite testing framework, such as Mocha, or Jest.

Recommendations and best practices

  • Make assertions on both server-side and client-side structures after simulating an exchange of messages between them.
  • Room instances are disposed between tests - make sure each test you declare is creating the room you’re going to perform tests and don’t re-use room instances between tests.
  • Always declare your test cases with async.
  • Before running assertions make sure you await for the server or client to process pending actions
App.test.ts
import { ColyseusTestServer, boot } from "@colyseus/testing";
 
// import your "app.config.ts" file here.
import appConfig from "../src/app.config";
 
describe("testing your Colyseus app", () => {
  let colyseus: ColyseusTestServer;
 
  before(async () => colyseus = await boot(appConfig));
  after(async () => colyseus.shutdown());
 
  beforeEach(async () => await colyseus.cleanup());
 
  it("connecting into a room", async() => {
    // `room` is the server-side Room instance reference.
    const room = await colyseus.createRoom("my_room", {});
 
    // `client1` is the client-side `Room` instance reference (from the JavaScript SDK)
    const client1 = await colyseus.connectTo(room);
 
    // make your assertions
    assert.strictEqual(client1.sessionId, room.clients[0].sessionId);
  });
});

Waiting for asynchronous actions to process

When sending messages between server and client, it’s important to wait for the server to process the message before making assertions.

Wait For Message Type

Wait for a particular message type to arrive in the server.

Signature
room.waitForMessage(type): Promise<[Client, any]>
App.test.ts
it("should receive message", async() => {
    const room = await colyseus.createRoom("my_room");
    const client1 = await colyseus.connectTo(room);
 
    client1.send("foo", "payload");
 
    // wait for specific a message
    const [ client, message ] = await room.waitForMessage("foo");
 
    // ... message "foo" has been received and processed
    assert.strictEqual(client.sessionId, client1.sessionId);
    assert.strictEqual("payload", message);
});

Wait For Next Message

Wait for any next message to arrive in the server.

Signature
room.waitForNextMessage(delay?: number): Promise<[Client, any]>

Parameters

  • delay: number - additional delay after onMessage has been called in the server-side, in milliseconds (optional)
App.test.ts
it("should receive message", async() => {
    const room = await colyseus.createRoom("my_room");
    const client1 = await colyseus.connectTo(room);
 
    let received = false;
    room.onMessage("foo", (client, message) => {
        received = true;
    });
 
    client1.send("foo");
    await room.waitForNextMessage();
 
    assert.ok(received);
});

Wait For Next Patch

Wait for the server to send the latest patched state to all clients.

Signature
room.waitForNextPatch(): Promise<void>
App.test.ts
it("client state must match server's after patch is received", async() => {
    const room = await colyseus.createRoom("my_room");
    const client1 = await colyseus.connectTo(room);
 
    await room.waitForNextPatch();
 
    // server and client's state must match after the patch
    assert.deepStrictEqual(client1.state.toJSON(), room.state.toJSON());
});

Wait For Next Simulation Tick

Wait for the next simulation tick to complete.

Signature
room.waitForNextSimulationTick(): Promise<void>
App.test.ts
it("should assert something after room's simulation tick", async() => {
    const room = await colyseus.createRoom("my_room");
    const client1 = await colyseus.connectTo(room);
 
    await room.waitForNextSimulationTick();
 
    // (this is just an illustration scenario)
    // (assuming the room's state has a "tick" property that updates during setSimulationInterval())
    assert.strictEqual(room.state.tick, 1);
});

Client-side SDK methods & utilities

From your test-cases, you may call any of the client-side SDK methods through colyseus.sdk

App.test.ts
it("should connect into battle_room with options x, y, z", async () => {
    const client = await colyseus.sdk.joinOrCreate("battle_room", {
        a: "a",
        b: "b",
        c: "c"
    });
    assert.ok(client.sessionId);
});

Wait for Next Patch

Wait for client state to be in sync with the server.

Signature
client.waitForNextPatch(): Promise<void>
App.test.ts
it("should do xyz after receiving message 'x'", async () => {
    const client = await colyseus.sdk.joinOrCreate("battle_room");
    await client.waitForNextPatch();
    // perform assertions after client has received a message
});

Wait for Message Type

Wait for a particular message type to arrive in the client-side.

Signature
client.waitForMessage(type): Promise<any>
App.test.ts
it("should do xyz after receiving message 'x'", async () => {
    const client = await colyseus.sdk.joinOrCreate("battle_room");
    client.send("ask-for-x");
 
    await client.waitForMessage("received-x");
    // perform assertions after client has received "received-x" message type.
});

Wait for Next Message

Wait for any next message to arrive in the client.

Signature
client.waitForNextMessage(): Promise<any>
App.test.ts
it("should do xyz after receiving message 'x'", async () => {
    const client = await colyseus.sdk.joinOrCreate("battle_room");
    client.send("ask-for-x");
 
    await client.waitForNextMessage();
    // perform assertions after client has received a message
});

Testing HTTP Routes

The @colyseus/testing also offers an HTTP client for requesting your custom http routes:

  • colyseus.http.get(url, opts)
  • colyseus.http.post(url, opts)
  • colyseus.http.patch(url, opts)
  • colyseus.http.delete(url, opts)
  • colyseus.http.put(url, opts)
App.test.ts
it("should get json data", async () => {
    const response = await colyseus.http.get("/");
 
    // "data" is the response body
    assert.deepStrictEqual({ success: true }, response.data);
 
    // access to response headers.
    assert.strictEqual('header value', response.headers['some-header']);
});
Last updated on