Q:

Testing client connection on the server side with websockets in mocha

I made a simple example app using socket.io library and now I would like to test that my io.on("connection") is called when a client connects. 
So I created a simple fake client in my unit test that sucessfully connects to the server, but the spy that watches the method handleSocket triggered by io.on("connection") says it has never been triggered, which it clearly has been according to the logs.
It is probably a timing/async issue that I can't figure out. 
Server-side code:
const express = require("express");
const app = express();
const port = process.env["PORT"] || 3030;
var io = require("socket.io").listen(port);
io.on("connect", socket => handleSocket(socket));
function handleSocket(socket) {
  console.log("a user connected", socket.id);
}
exports.io = io;
exports.handleSocket = handleSocket;

The actual unit test in mocha:
var expect = require("chai").expect;
var sinon = require("sinon");
var ioClient = require("socket.io-client");
process.env["PORT"] = 3030;
process.env["VERBOSE"] = true;
var options = {
  transports: ["websocket"],
  "force new connection": false
};
describe("server.spec", function() {
  this.timeout(5000);
  it("Client should connect", function(done) {
    app = require("./server2");
    let spy = sinon.spy(app.handleSocket);
    var client = ioClient.connect("http://localhost:" + process.env["PORT"], options);
    client.on("connect", function() {
      expect(spy.called).to.be.equal(true);
      client.disconnect();
      app.io.close();
      done();
    });
  });
});

Test - fails, the spy.called is equal to false 
Log output:
  server.spec
a user connected jopFRhcfzjkC_TjsAAAA
Spy was called: false
    1) Client should connect
a user connected uIpW2gsiljJkS4wFAAAB
Spy was called: false

A:

I think the documentation here: http://sinonjs.org/releases/v4.5.0/spies/, in the section on creating spies is a little unclear for this use case:
var spy = sinon.spy(myFunc);

If you create a spy that way it means that spy is it self the function you would need to call. I.e., it creates a proxy around myFunc, but it does not actually use that proxy if you call myFunc directly. What you need is this: var spy = sinon.spy(object, "method");, so your example would become:
Server Code:
const express = require("express");
const app = express();
const port = process.env["PORT"] || 3030;
var io = require("socket.io").listen(port);

exports.handleSocket = function(socket) {
  console.log("a user connected", socket.id);
};

// NOTE: we use handleSocket as method of exports here
io.on("connect", socket => exports.handleSocket(socket));

exports.io = io;

test spec:
var expect = require("chai").expect;
var sinon = require("sinon");
var ioClient = require("socket.io-client");
process.env["PORT"] = 3030;
process.env["VERBOSE"] = true;
var options = {
  transports: ["websocket"],
  "force new connection": false
};
describe("server.spec", function() {
  this.timeout(5000);
  it("Client should connect", function(done) {
    app = require("./server2");
    let spy = sinon.spy(app, "handleSocket"); // <- CHANGED CODE
    var client = ioClient.connect("http://localhost:" + process.env["PORT"], options);
    client.on("connect", function() {
      expect(spy.called).to.be.equal(true);
      client.disconnect();
      app.io.close();
      done();
    });
  });
});

When you create a spy this way it actually replaces the method (handleSocket) on the object (app) which is the same object as the exported object from your server code.

