web-dev-qa-db-fra.com

Test unitaire Node.js et WebSockets (Socket.io)

Quelqu'un pourrait-il fournir un test unitaire solide et simple pour Node.js en utilisant WebSockets (Socket.io)?

J'utilise socket.io pour Node.js et j'ai examiné socket.io-client pour établir la connexion client à un serveur dans le test. Cependant, il me semble manquer quelque chose.

Dans l'exemple ci-dessous, "travaillé ..." n'est jamais imprimé.

var io = require('socket.io-client')
, assert = require('assert')
, expect = require('expect.js');

describe('Suite of unit tests', function() {

    describe('First (hopefully useful) test', function() {

        var socket = io.connect('http://localhost:3001');
        socket.on('connect', function(done) {
            console.log('worked...');
            done();
        });

        it('Doing some things with indexOf()', function() {
            expect([1, 2, 3].indexOf(5)).to.be.equal(-1);
            expect([1, 2, 3].indexOf(0)).to.be.equal(-1);
        });

    });
});

Au lieu de cela, je reçois simplement:

  Suite of unit tests
    First (hopefully useful) test
      ✓ Doing some things with indexOf() 


  1 test complete (26 ms)

Aucune suggestion?

58
mysterlune

Après avoir poussé et poussé davantage, j'ai trouvé des informations incroyablement utiles sur http://blog.foundry376.com/2012/09/connecting-to-a-socket-io-server-from-node-js-unit -tests . Dans l'exemple de l'auteur, il souligne l'étape critique de l'établissement d'écouteurs de socket dans les crochets "avant *". Cet exemple fonctionne (en supposant qu'un serveur écoute les connexions de socket sur localhost: 3001, bien sûr)

var io = require('socket.io-client')
, assert = require('assert')
, expect = require('expect.js');

describe('Suite of unit tests', function() {

    var socket;

    beforeEach(function(done) {
        // Setup
        socket = io.connect('http://localhost:3001', {
            'reconnection delay' : 0
            , 'reopen delay' : 0
            , 'force new connection' : true
        });
        socket.on('connect', function() {
            console.log('worked...');
            done();
        });
        socket.on('disconnect', function() {
            console.log('disconnected...');
        })
    });

    afterEach(function(done) {
        // Cleanup
        if(socket.connected) {
            console.log('disconnecting...');
            socket.disconnect();
        } else {
            // There will not be a connection unless you have done() in beforeEach, socket.on('connect'...)
            console.log('no connection to break...');
        }
        done();
    });

    describe('First (hopefully useful) test', function() {

        it('Doing some things with indexOf()', function(done) {
            expect([1, 2, 3].indexOf(5)).to.be.equal(-1);
            expect([1, 2, 3].indexOf(0)).to.be.equal(-1);
            done();
        });

        it('Doing something else with indexOf()', function(done) {
            expect([1, 2, 3].indexOf(5)).to.be.equal(-1);
            expect([1, 2, 3].indexOf(0)).to.be.equal(-1);
            done();
        });

    });

});

J'ai trouvé que le placement de done () dans l'écouteur beforeEach, socket.on ('connect' ...) était crucial pour établir la connexion. Par exemple, si vous commentez done () dans l'écouteur, puis que vous lui ajoutez une portée (juste avant de quitter le beforeEach), vous verrez le message "pas de connexion à rompre ..." au lieu du message "se déconnecter .. ." message. Ainsi:

beforeEach(function(done) {
    // Setup
    socket = io.connect('http://localhost:3001', {
        'reconnection delay' : 0
        , 'reopen delay' : 0
        , 'force new connection' : true
    });
    socket.on('connect', function() {
        console.log('worked...');
        //done();
    });
    socket.on('disconnect', function() {
        console.log('disconnected...');
    });
    done();
});

Je suis nouveau dans Mocha, il y a donc probablement une raison très évidente pour les initiés de placer done () dans la portée du socket lui-même. Espérons que ce petit détail sauvera les autres dans mes chaussures de l'arrachage des cheveux.

Pour moi, le test ci-dessus (avec une portée correcte de done ()) sort:

  Suite of unit tests
    First (hopefully useful) test
      ◦ Doing some things with indexOf(): worked...
      ✓ Doing some things with indexOf() 
disconnecting...
disconnected...
      ◦ Doing something else with indexOf(): worked...
      ✓ Doing something else with indexOf() 
disconnecting...
disconnected...


  2 tests complete (93 ms)
62
mysterlune

Offrant une extension de la réponse acceptée ici. Possède une communication client-serveur de base utile comme passe-partout pour d'autres tests futurs. Utilisation de mocha, chai et expect.

var io = require('socket.io-client')
  , io_server = require('socket.io').listen(3001);

describe('basic socket.io example', function() {

  var socket;

  beforeEach(function(done) {
    // Setup
    socket = io.connect('http://localhost:3001', {
      'reconnection delay' : 0
      , 'reopen delay' : 0
      , 'force new connection' : true
      , transports: ['websocket']
    });

    socket.on('connect', () => {
      done();
    });

    socket.on('disconnect', () => {
      // console.log('disconnected...');
    });
  });

  afterEach((done) => {
    // Cleanup
    if(socket.connected) {
      socket.disconnect();
    }
    io_server.close();
    done();
  });

  it('should communicate', (done) => {
    // once connected, emit Hello World
    io_server.emit('echo', 'Hello World');

    socket.once('echo', (message) => {
      // Check that the message matches
      expect(message).to.equal('Hello World');
      done();
    });

    io_server.on('connection', (socket) => {
      expect(socket).to.not.be.null;
    });
  });

});
7
helsont

Découvrez cette solution passe-partout basée sur les promesses et bonne pratique . Vous pouvez tester vos serveurs avec des événements io entiers, sans transpiration. Il vous suffit de copier un test standard et d'ajouter votre propre code si nécessaire.

Consultez le dépôt sur GitHub pour le code source complet.

https://github.com/PatMan10/testing_socketIO_server

const io = require("socket.io-client");
const ev = require("../utils/events");
const logger = require("../utils/logger");

// initSocket returns a promise
// success: resolve a new socket object
// fail: reject a error
const initSocket = () => {
  return new Promise((resolve, reject) => {
      // create socket for communication
      const socket = io("localhost:5000", {
        "reconnection delay": 0,
        "reopen delay": 0,
        "force new connection": true
      });

      // define event handler for sucessfull connection
      socket.on(ev.CONNECT, () => {
        logger.info("connected");
        resolve(socket);
      });

      // if connection takes longer than 5 seconds throw error
      setTimeout(() => {
        reject(new Error("Failed to connect wihtin 5 seconds."));
      }, 5000);
    }
  );
};


// destroySocket returns a promise
// success: resolve true
// fail: resolve false
const destroySocket = socket => {
  return new Promise((resolve, reject) => {
    // check if socket connected
    if (socket.connected) {
      // disconnect socket
      logger.info("disconnecting...");
      socket.disconnect();
      resolve(true);
    } else {
      // not connected
      logger.info("no connection to break...");
      resolve(false);
    }
  });
};

describe("test suit: Echo & Bello", () => {
  test("test: ECHO", async () => {
    // create socket for communication
    const socketClient = await initSocket();

    // create new promise for server response
    const serverResponse = new Promise((resolve, reject) => {
      // define a handler for the test event
      socketClient.on(ev.res_ECHO, data4Client => {
        //process data received from server
        const { message } = data4Client;
        logger.info("Server says: " + message);

        // destroy socket after server responds
        destroySocket(socketClient);

        // return data for testing
        resolve(data4Client);
      });

      // if response takes longer than 5 seconds throw error
      setTimeout(() => {
        reject(new Error("Failed to get reponse, connection timed out..."));
      }, 5000);
    });

    // define data 4 server
    const data4Server = { message: "CLIENT ECHO" };

    // emit event with data to server
    logger.info("Emitting ECHO event");
    socketClient.emit(ev.com_ECHO, data4Server);

    // wait for server to respond
    const { status, message } = await serverResponse;

    // check the response data
    expect(status).toBe(200);
    expect(message).toBe("SERVER ECHO");
  });

  test("test BELLO", async () => {
    const socketClient = await initSocket();
    const serverResponse = new Promise((resolve, reject) => {
      socketClient.on(ev.res_BELLO, data4Client => {
        const { message } = data4Client;
        logger.info("Server says: " + message);
        destroySocket(socketClient);
        resolve(data4Client);
      });

      setTimeout(() => {
        reject(new Error("Failed to get reponse, connection timed out..."));
      }, 5000);
    });

    const data4Server = { message: "CLIENT BELLO" };
    logger.info("Emitting BELLO event");
    socketClient.emit(ev.com_BELLO, data4Server);

    const { status, message } = await serverResponse;
    expect(status).toBe(200);
    expect(message).toBe("SERVER BELLO");
  });
});

---- Note de bas de page ----

Selon la façon dont vous configurez votre environnement de serveur, vous pouvez rencontrer des conflits environnementaux entre socket.io et socket.io-client s'exécutant à partir du même projet simultanément. Dans ce cas, il serait préférable de séparer le projet en un "client test" et un serveur. Commander ci-dessous repo si vous rencontrez ce problème.

https://github.com/PatMan10/testing_socketIO_server_v2

2
PatMan10

J'ai eu ce problème: comment faire un test unitaire avec un "socket.io-client" si vous ne savez pas combien de temps le serveur met pour répondre?.

J'ai résolu ainsi en utilisant mocha et chai:

var os = require('os');
var should = require("chai").should();
var socketio_client = require('socket.io-client');

var end_point = 'http://' + os.hostname() + ':8081';
var opts = {forceNew: true};

describe("async test with socket.io", function () {
this.timeout(10000);

it('Response should be an object', function (done) {
    setTimeout(function () {
        var socket_client = socketio_client(end_point, opts);  

        socket_client.emit('event', 'ABCDEF');

        socket_client.on('event response', function (data) {
            data.should.be.an('object');
            socket_client.disconnect();
            done();
        });

        socket_client.on('event response error', function (data) {
            console.error(data);
            socket_client.disconnect();
            done();
            });
        }, 4000);
    });
});
2
juanmiguelRua