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.
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
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.
room.waitForMessage(type): Promise<[Client, any]>
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.
room.waitForNextMessage(delay?: number): Promise<[Client, any]>
Parameters
delay: number
- additional delay afteronMessage
has been called in the server-side, in milliseconds (optional)
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.
room.waitForNextPatch(): Promise<void>
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.
room.waitForNextSimulationTick(): Promise<void>
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
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.
client.waitForNextPatch(): Promise<void>
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.
client.waitForMessage(type): Promise<any>
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.
client.waitForNextMessage(): Promise<any>
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)
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']);
});