integration tests with chai and mocha

All the goodies that you are building need to be of highest quality. But how is quality possible when no tests are in place?

If you have followed along, this very website promotes test driven development, very passionately if you ask me. I want my code to be thoroughly tested, I want confidence, I want speed. Remember, test driven development is the practice that makes you agile, in terms of speedy development and ability to cope with the code’s evolution. You go faster when you write tests.

That is no different when we code real time applications, in this particular instance, with, which heavily relies on an event driven style. We need to make sure that our channels, our business rules and the wiring work properly and that is exactly the story that I am going to tell you, so let’s dive in.


What the heck is is a JavaScript framework which builds on WebSockets technology.
From MDN, here’s a quote for WebSockets:

WebSockets is an advanced technology that makes it possible to open an interactive communication session between the user’s browser and a server. With this API, you can send messages to a server and receive event-driven responses without having to poll the server for a reply.

But why we bother using The answer lies to browser compatibility. Yeah, old story, not all browsers support this kind of technology, so that’s where comes into play.
Using this one you can eliminate issues on WebSocket connections. It is a simplified wrapper which creates and works with WebSockets. An abstraction layer upon them, like the jQuery of WebSockets, if you like.
And problems like disconnections, timeouts, heartbeats, old browser, long polling, they are gone, it takes care of them for us.

So, if I were to describe it, I would say that is an event-based framework, which allows bidirectional communication.
In simplified terms, my latter statement can be translated like: Users can connect to a socket and can send and receive data to connected sockets in real time.
Also, this framework loves the Pub/Sub pattern, so if you are familiar with it, you won’t have any issue, you will get used to it extremely fast.

By the way, has browser support for smartphone, desktop and tablet and we can say that almost 90% of browsers can use that without any problems at all.

A’right and what about Mocha?

That one is easy. It is a testing framework running on Node.js, which plays very well with asynchronous tests. The bread and butter of this very story is asynchronous tests, because it is the only way to test
So, your Node.js backend tests should be supported by Mocha.

Lastly, some short comments on chai assertion library. It will help you with assertions, it can work with the testing framework of your preference and also it can accommodate your testing style, meaning, you can test in BDD or TDD style, based on the assertion interface you will chose. I chose a TDD style and will work with chai.assert.

Tests will save you

Why we need tests?

That should be obvious, right?
No code should ship to production without its pairing tests. Period.

We need to test the business rules and the behavior that we are going to build, we need to know what happens when we fire an event from the browser, how the server acts on that.
We need to know how the client code is acting on socket server events.
We need to be able to refactor the code and be certain that it will work, test is going to let us know if we did break something.

What you should take away from this section is that tests are important, so do write them. Please. Por favor.

What type of tests are we going to write

Hmm, let’s think about for a moment.
We have two parts interacting, we have server and the client. The first is running on  the backend and the latter is running on browser, so we need to connect these two different worlds together. Obviously we are not testing a unit.
We need to setup a server and a client on each test. The client will publish an event to the server and if the server knows how to respond to that event, subscribers will catch that and execute a callback.
It should be obvious by now, that we are going to write integration tests, as they cover less depth (less depth means more paths and more classes/units to assert), we are far away from a single unit of code, we test multiple.

Notice that I have decorated in bold and italics the word ‘callback’. This is tricky, because a callback is not handled by the code we are writing, thus the test, it is handled by another entity, that fires that callback when its work is done. That means, our test is going to linearly execute and then exit, as that kind of execution that was described is asynchronous. The callback is not going to fire by that time, so the test is going to lie to us (no assertions at the end, assertions are only in the callback body, so the test will exit with a success code).

Thankfully, Jasmine ( I love this framework <3 ) provides us with asynchronous support.
Your it, beforeEach, etc. methods can take an optional argument which signals Jasmine that the asynchronous operation has ended, thus the test (or the setup methods) can terminate.
By default it awaits for 5 seconds before it nukes your test. When your asynchronous code is over, you just call this argument like a method and you are done. If you want to fail and complete the spec, you call its fail method.


This is a successful operation

it("should terminate successfully", (done) => {
myPromise.then((value) => {
// assert the value
done(); // Terminates the test
view raw success.ts hosted with ❤ by GitHub

This is an operation which you want to fail for some reason, but deliberately, not because of timeout

it("should fail deliberately", (done) => {
myPromise.then((value) => {"Because I can...");
view raw fail.ts hosted with ❤ by GitHub

Please note that if you do not provide the done argument, the test(s) above will fail either way, with a timeout reason.

You show me yours, I’ll show you mine

Enough with the talking, it’s time to look more on the code and get our hands dirty.


For this one, at minimum we need:

  • Jasmine
  • Mocha
  • Chai


Take a look at my package.json file for reference (for brevity, I have omitted some code).
I use Jasmine 2 and 2
Of course, because I use Typescript, I have downloaded the appropriate types as well

"scripts": {
"test": "tsc && karma start karma.conf.js",
"test:dev": "gulp test:dev",
"test:travis": "gulp test:travis",
"test:e2e:dev": "tsc && protractor src/e2e/conf.js",
"test:backend:dev": "gulp mocha:dev",
"test:backend:travis": "gulp mocha:travis",
"start": "if-env NODE_ENV=production && npm run start:prod || npm run start:dev",
"start:dev": "gulp app:dev",
"pre:start:prod": "gulp app:prod",
"start:prod": "npm run server",
"server": "node app.js"
"dependencies": {
"angular": "^1.6.4",
"angular-animate": "^1.6.4",
"angular-sanitize": "^1.6.4",
"angular-ui-bootstrap": "^2.5.0",
"angular-ui-router": "^1.0.0-rc.1",
"body-parser": "^1.17.2",
"bootstrap": "^3.3.7",
"bootstrap-sass": "^3.3.7",
"browserify": "^13.1.0",
"core-js": "^2.4.1",
"express": "^4.14.0",
"font-awesome": "^4.6.3",
"jquery": "^3.2.1",
"ngstorage": "^0.3.11",
"": "^2.0.2",
"": "^2.0.2",
"toastr": "^2.1.2",
"uuid": "^3.0.1"
"devDependencies": {
"@types/angular": "^1.6.17",
"@types/angular-animate": "^1.5.6",
"@types/angular-mocks": "^1.5.9",
"@types/angular-ui-bootstrap": "^0.13.42",
"@types/angular-ui-router": "^1.1.37",
"@types/angularlocalstorage": "^0.1.30",
"@types/body-parser": "^1.16.3",
"@types/chai": "^4.0.0",
"@types/chai-http": "0.0.30",
"@types/express": "^4.0.35",
"@types/express-serve-static-core": "^4.0.45",
"@types/gulp": "^4.0.3",
"@types/jasmine": "^2.5.47",
"@types/jquery": "^2.0.46",
"@types/mime": "0.0.29",
"@types/node": "^7.0.22",
"@types/orchestrator": "^0.3.0",
"@types/q": "^1.0.0",
"@types/serve-static": "^1.7.31",
"@types/": "^1.4.29",
"@types/": "^1.4.29",
"@types/supertest": "^2.0.0",
"@types/toastr": "^2.1.33",
"@types/uuid": "^2.0.29",
"@types/vinyl": "^2.0.0",
"angular-mocks": "^1.5.8",
"babel-core": "6.10.4",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babelify": "^7.3.0",
"browser-sync": "^2.14.0",
"browserify-istanbul": "^2.0.0",
"chai": "^4.0.2",
"chai-http": "^3.0.0",
"del": "^2.2.2",
"gulp": "^3.9.1",
"gulp-concat": "^2.6.0",
"gulp-cssnano": "^2.1.2",
"gulp-express": "^0.3.5",
"gulp-mocha": "^4.3.1",
"gulp-nodemon": "^2.1.0",
"gulp-open": "^2.0.0",
"gulp-sass": "^2.3.2",
"gulp-sourcemaps": "^2.6.0",
"gulp-typescript": "^3.1.7",
"gulp-uglify": "^3.0.0",
"gulp-uglifycss": "^1.0.8",
"gulp-util": "^3.0.8",
"iconv-lite": "^0.4.17",
"if-env": "^1.0.0",
"isparta": "^4.0.0",
"istanbul": "^0.4.5",
"jasmine": "^2.5.2",
"jasmine-core": "^2.5.2",
"karma": "^1.7.0",
"karma-babel-preprocessor": "^6.0.1",
"karma-browserify": "^5.1.1",
"karma-chrome-launcher": "^2.1.1",
"karma-coverage": "^1.1.1",
"karma-htmlfile-reporter": "^0.1.2",
"karma-jasmine": "^1.1.0",
"karma-mocha-reporter": "^2.2.3",
"karma-ng-html2js-preprocessor": "^1.0.0",
"merge-stream": "^1.0.0",
"mocha": "^3.4.2",
"path": "^0.12.7",
"protractor": "^4.0.14",
"regexify": "^2.2.0",
"require-dir": "^0.3.0",
"run-sequence": "^1.2.2",
"sass": "^0.5.0",
"socket-io-mock": "^1.0.1",
"supertest": "^3.0.0",
"tsify": "^3.0.1",
"typescript": "^2.3.3",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0",
"watchify": "^3.9.0",
"xml2js": "^0.4.17"
view raw package.json hosted with ❤ by GitHub

Configuring mocha

Looking at the package.json above, the command for local testing is test:backend:dev, which in turn uses Gulp to configure mocha and fire the test runner.
Let’s take a look.

var gulp = require("gulp"),
gutil = require("gulp-util"),
mocha = require("gulp-mocha"),
sequence = require("run-sequence");
var mochaRun = function () {
return gulp.src("src/app/server/tests/**/*.js", { read: false })
.pipe(mocha({ timeout: 10000, reporter: "list" }))
.on("error", gutil.log);
gulp.task("mocha", function () {
return mochaRun();
gulp.task("mocha:dev", ["ts"], function (callback) {"src/app/server/**/*.ts", function () {
sequence("ts", "mocha");
gulp.task("mocha:travis", ["ts"], function (callback) {
if (process.env.TRAVIS) {
return mochaRun();
else {
gutil.log(gutil.colors.bgRed("Fatal error. CI tests will run only in TRAVIS environment"));
view raw tasks.test.js hosted with ❤ by GitHub

I use gulp-mocha module here, which is only a wrapper around mocha, but helps me greatly to build this task.
So, I create a function, in order to reuse it in mocha:dev and mocha:travis tasks. Let’s focus on the mocha:dev task, which runs the tests locally.
What I do here, is to run the Typescript compiler before the task executes (the ["ts"] dependency), then watch all the Typescript files under a certain directory and when a change happens, run the Typescript compiler and mocha in order.

Setting up tests, configuring

Next step is to configure tests to create a server and a client. After that, we can proceed to tests.
Following code shows a basic setup. I import the server and, I create some configuration for the client and then I instantiate them, first the server which listens to a specific port (5000 in this case) and then the client which connects to the server with the aforementioned options.

import * as ioClient from "";
import * as io from "";
import * as chai from "chai";
import { Socket } from "../";
import { UserRole } from "../../domain/index";
import {
RoomCreateEvent, RoomGetAllEvent, InternalServerErrorEvent, RoomShowAllEvent,
RoomsAllEvent, UsersAllEvent, RoomNotFoundEvent, RoomJoinEvent, RequestAllRoomsEvent,
RequestAllUsersEvent, RoomDisconnectEvent, UserDisconnectedEvent, BanEvent, UserBannedEvent
} from "../../domain/events/index";
const assert = chai.assert;
const socketUrl: string = "http://localhost:5000";
const options: SocketIOClient.ConnectOpts = {
transports: ['websocket'],
'force new connection': true
describe("Server", () => {
describe("Socket", () => {
let server: SocketIO.Server;
let socket: Socket;
let client: SocketIOClient.Socket;
beforeEach(() => {
server = io().listen(5000);
socket = new Socket(server);
client = ioClient.connect(socketUrl, options);
afterEach(() => {
// more tests coming...
view raw socket.spec.ts hosted with ❤ by GitHub

Let me explain some parts of it:

  • Line 13: I choose to use the assert style from chai (I like more a TDD approach on this).
    Should be used like assert.typeOf(foo, 'string');
  • Line 14-18: I set the socketUrl to be on localhost and listen to port 5000. This is the same port I am going to provide to the socket server to listen to.
    The options are meant for the, they are pretty much standard and self-explanatory.
  • Line 27: Now, in beforeEach() method I create a new instance of the server and the client. In line 27 I create a new server which listens to port 5000.
  • Line 28-29: I have a custom class named Socket, which receives an io server, so I pass it there and call the connect() method of that class, which only registers events using the .on() function of the server (more on this later).
  • Line 30: I create a new client which listens to the socketUrl and takes the options I defined earlier.
  • Line 33-36: In afterEach() function, which is called after each spec is completed, I close the connection of server and the client. I do this in order to not bleed invocations to listeners which may be set on other tests, so I make sure I test in isolation.

Action baby

Time for some tests. Before I do that, as promised, let’s check on the Socket class and some of its code.

import * as uuidV1 from "uuid/v1";
import { Constants } from "./constants";
import { getFirst, filter, isUndefined, Guard } from "../../shared/index";
import { Room, User, UserRole, Roles } from "../../domain/index";
import {
InternalServerErrorEvent, RoomsFullEvent, RoomShowAllEvent, RoomDisconnectEvent,
RoomNotFoundEvent, UserBannedEvent, UserDisconnectedEvent, RoomCreateEvent,
BanEvent, RequestAllRoomsEvent, RequestAllUsersEvent, RoomGetAllEvent,
RoomJoinEvent, RoomsAllEvent, UsersAllEvent, ConnectionEvent
} from "../../domain/events/index";
const max: number = (<ServerAppConfig.ServerConfiguration>require("../server.config.json")).socketio.maxRoomsAllowed;
export class Socket {
private rooms: Room[] = [];
constructor(private io: SocketIO.Server) { }
public connect() {, (socket: ISocket) => {
socket.on(RoomCreateEvent.eventName, (data: CreateRoomEventArgs, callback: CreateRoomEventCallback) => {
try {
createRoom(data, callback);
} catch (error) {
callback({ access: false });
this.emitInternalServerError(socket, error);
let createRoom = (data: CreateRoomEventArgs, callback: CreateRoomEventCallback) => {
Guard.throwIfObjectUndefined(data, Constants.dataIsRequired);
Guard.throwIfStringNotDefinedOrEmpty(, Constants.dataNameIsRequired);
let isCurrentUserInAnyOtherRoom = this.rooms.filter(r => !!r.getUser( > 0;
Guard.validate(isCurrentUserInAnyOtherRoom, Constants.youAreAlreadyInAnotherRoom);
if (this.hasRoomsReachedMaxLimit()) {
callback({ access: false });
let room = this.createRoom();
this.emitLoggedUsersToRoom(socket, room);
callback({ access: true, roomId: });
// More handlers in same manner. just look at the repository if you want more...
view raw Socket.ts hosted with ❤ by GitHub

When code calls the connect() method, events are subscribed in the server, like the RoomCreateEvent. When the client code emits that event, server will invoke this callback.
So, looking at the RoomCreateEvent, we can see that it receives some kind of data, of type CreateRoomEventArgs and a callback, which callback returns a boolean and an optional string.

declare interface CreateRoomEventCallback {
($data: CreateRoomCallbackArgs): void
declare interface CreateRoomCallbackArgs {
access: boolean,
roomId?: string
view raw index.d.ts hosted with ❤ by GitHub

Now let’s focus on the tests. First, let’s write a test to make sure it can connect to the socket.

describe("connect", () => {
it("should connect socket", (done: Function) => {
client.on("connect", () => {
assert.equal(client.connected, true);
view raw socket.spec.ts hosted with ❤ by GitHub

First thing I do is to subscribe to the "connect" event for the client, so as soon as the client is connected I can proceed with my tests, like setting or emitting other events, and so on and so forth. So, the client connects, I assert the client.connected property to make sure he is connected and then I call the done(); method to terminate the test.
Notice that I know this test is valid because it terminates successfully and all because of the done argument. If I hadn’t call this method the test would fail with a timeout reason.

Good, let’s proceed in more advanced techniques now. I want to test the functionality on RoomCreateEvent event. Let’s see some examples

describe("room-create", () => {
it("should emit internal-server-error event for undefined data", (done: Function) => {
// arrange
let expectedParameter: string = "name";
client.on("connect", () => {
// assert
client.on(InternalServerErrorEvent.eventName, (error: Exception) => {
assert.equal(`Parameter data is required`, error.message);
// act
client.emit(RoomCreateEvent.eventName, undefined, ($value: CreateRoomCallbackArgs) => {
assert.equal(false, $value.access);
view raw socket.spec.ts hosted with ❤ by GitHub

As you can see, asynchronous tests are not following the norm of normal tests, you declare your callback first, but you have your assertion logic there, I know, it looks weird.
So, this test case wants to test if the server will respond with an InternalServerErrorEvent if I pass undefined input data.

So, as usual, I subscribe to the "connect" event. In  its callback now, first I subscribe to the InternalServerErrorEvent, which is the event that I expect the server to respond with. My assertions are all there, in its callback, I assert the exception object that is returned and I terminate the test, calling the done() method.

In order for the InternalServerErrorEvent callback to fire, I need to emit it somehow.
Fortunately, my server emits that (if you check at line 25 of Socket class code, you will see the following line which indeed emits an event to whoever is subscribing to it.
this.emitInternalServerError(socket, error);

Notice that I assert the return value of the RoomCreateEvent callback. That is perfectly legal, if you check at line 24 of Socket class, you will notice that I indeed call the callback and pass an object with a false value on access property
callback({ access: false });
But my test will not terminate there, it will terminate at InternalServerErrorEvent callback.

Let’s look at one more test.
In the following, I emit the RoomCreateEvent with some valid data. My expectation is to:

  • Receive an object with access property set to true and roomId property set to the roomId that was generated.
  • Publish 3 events, which I expect to inform me that one user is listed into that room, I have 1 active room in total and 1 active user in total.
    • RoomShowAllEvent. Returns the number of users connected to that room.
    • RoomsAllEvent. Returns the total number of active rooms.
    • UsersAllEvent. Returns the total number of users in all rooms.
it("should emit 'room-show-all', 'rooms-all' and 'users-all' events when creating new room", (done: Function) => {
// arrange
let roomCreateEventArgs: { name: string } = { name: "George" };
let roomCreateEvent = new RoomCreateEvent(roomCreateEventArgs);
client.on("connect", () => {
// assert
client.on(RoomShowAllEvent.eventName, (users: UserRole[]) => {
assert.equal(1, users.length);
// assert
client.on(RoomsAllEvent.eventName, (rooms: number) => {
assert.equal(1, rooms);
// assert
client.on(UsersAllEvent.eventName, (users: number) => {
assert.equal(1, users);
// act
client.emit(RoomCreateEvent.eventName,, ($value: CreateRoomCallbackArgs) => {
assert.equal(true, $value.access);
view raw socket.spec.ts hosted with ❤ by GitHub

In test, as I mentioned earlier, I first set my subscribers then I publish the event I am looking for. I publish the event with the emit method at the bottom of the test.

First I subscribe to the RoomShowAllEvent.
Then, I subscribe to RoomsAllEvent.
Finally, I subscribe to UsersAllEvent. This one terminates the test.

I did this on purpose, because this is the order they are executed in the createRoom method from Socket class. Let me refresh your memory with a snippet:

{ // In createRoom method (line 29-55)
// Events are emitted in this order
this.emitLoggedUsersToRoom(socket, room);
callback({ access: true, roomId: });
// method declarations...
private emitLoggedUsersToRoom(socket: ISocket, room: Room) {
let roomShowAllEvent = new RoomShowAllEvent(room.users);, roomShowAllEvent.users);
private emitRoomsAllEventWithTotalRooms() {, this.rooms.length);
private emitUsersAllEventWithTotalUsersInAllRooms() {, this.rooms.length ? this.getAllUsersFromAllRooms() : 0);
view raw socket.ts hosted with ❤ by GitHub

I just followed the normal flow. This can make the reader understand better my intention, but it is also practical. The last event that is fired is the UsersAllEvent, so it makes sense to terminate the test when it is fired. If I called the done() in let’s say RoomShowAllEvent event, then the RoomsAllEvent and UsersAllEvent callbacks wouldn’t fire and as a result their assertions would not assert. Just make sure you end the test on the last callback that will run, find your exit point. 🙂

For more advanced techniques and tests, like having multiple clients connect and how they would interact, take a look on my Github repository, you will find over 2000 lines of code and 92 tests only for this implementation.


Use Mocha for your Node.js tests and make sure you support asynchronous testing.
From that point on, if you get familiar with the API, tests won’t be that hard to author, you just need to emit client events to the server and assert server’s callback. You might also need to register to client events also, if your server emits events that the client should listen to.

Don’t forget to call the done optional argument after your last callback, so you can mark your tests as successful, else you will have timeout issues and of course test failures.

If you want to see more, or get some ideas, please feel free to take a look at my repository, here, you can find tests on server and other useful stuff. Disclaimer, the work is in progress on that one.

If you liked this blog post, please like, share and subscribe! For more, follow me on Twitter @giorgosdyrra.