单元测试 Node.js 和 WebSockets (Socket.io)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15509231/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 13:55:56  来源:igfitidea点击:

Unit testing Node.js and WebSockets (Socket.io)

node.jsunit-testingwebsocketsocket.io

提问by mysterlune

Could anyone provide a rock-solid, dead-simple unit test for Node.js using WebSockets (Socket.io)?

任何人都可以使用 WebSockets (Socket.io) 为 Node.js 提供坚如磐石、极其简单的单元测试吗?

I'm using socket.io for Node.js, and have looked at socket.io-client for establishing the client connection to a server in the test. However, I seem to be missing something.

我正在将 socket.io 用于 Node.js,并查看了 socket.io-client 以在测试中建立与服务器的客户端连接。然而,我似乎错过了一些东西。

In the example below, "worked..." never gets printed out.

在下面的例子中,“worked...”永远不会被打印出来。

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);
        });

    });
});

Instead, I simply get:

相反,我只是得到:

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


  1 test complete (26 ms)

Any suggestions?

有什么建议?

回答by mysterlune

After further poking and prodding, I found some incredibly useful information at http://blog.foundry376.com/2012/09/connecting-to-a-socket-io-server-from-node-js-unit-tests. In the author's example, he points out the critical step of establishing socket listeners in the "before*" hooks. This example works (assuming a server is listening for socket connections at localhost:3001, of course)

经过进一步的探索和刺激,我在http://blog.foundry376.com/2012/09/connecting-to-a-socket-io-server-from-node-js-unit-tests找到了一些非常有用的信息。在作者的例子中,他指出了在“before*”钩子中建立套接字监听器的关键步骤。此示例有效(当然,假设服务器正在侦听 localhost:3001 处的套接字连接)

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();
        });

    });

});

I found that the placement of done() in the beforeEach, socket.on('connect'...) listener was crucial to having the connection get established. For example, if you comment out done() in the listener, then add it one scope out (just before exiting the beforeEach), you'll see the "no connection to break..." message instead of the "disconnecting..." message. Like so:

我发现 done() 在 beforeEach, socket.on('connect'...) 侦听器中的放置对于建立连接至关重要。例如,如果您在侦听器中注释掉 done(),然后将其添加到一个范围之外(就在退出 beforeEach 之前),您将看到“无连接中断...”消息而不是“断开连接...”消息。 。“ 信息。像这样:

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();
});

I'm new to Mocha, so there's probably a very obvious reason to the initiated for placing done() withiin the socket scope itself. Hopefully that little detail will save others in my shoes from hair pulling.

我是 Mocha 的新手,因此将 done() 放置在套接字范围本身内可能有一个非常明显的原因。希望这个小细节能让我鞋子里的其他人免于拉扯头发。

For me, the above test (with correct scoping of done()) outputs:

对我来说,上面的测试(正确的 done() 范围)输出:

  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)

回答by helsont

Offering an extension of the accepted answer here. Has basic client to server communication useful as boilerplate for other future tests. Using mocha, chai, and expect.

在此处提供已接受答案的扩展。具有基本的客户端到服务器通信,可用作其他未来测试的样板。使用 mocha、chai 和 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;
    });
  });

});

回答by PatMan10

Check out this boilerplatesolution that's based on promisesand good practice. You can test your servers entire io events with it, no sweat. You just need to copy a boilerplate test and add your own code as needed.

查看这个基于承诺良好实践的样板解决方案。你可以用它测试你的服务器整个 io 事件,不用担心。您只需要复制样板测试并根据需要添加您自己的代码。

Checkout the repo on GitHub for full source code.

查看 GitHub 上的 repo 以获取完整源代码。

https://github.com/PatMan10/testing_socketIO_server

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");
  });
});

---- Foot Note ----

---- 脚注----

Depending on how you setup your server environment, you may experience environmental conflict between socket.io and socket.io-client running from the same project simultaneously. In which case it would be better to separate the project into a "test client" and a server. Checkout below repo if you get this issue.

根据您设置服务器环境的方式,您可能会遇到同时从同一项目运行的 socket.io 和 socket.io-client 之间的环境冲突。在这种情况下,最好将项目分为“测试客户端”和服务器。如果您遇到此问题,请查看以下 repo。

https://github.com/PatMan10/testing_socketIO_server_v2

https://github.com/PatMan10/testing_socketIO_server_v2

回答by juanmiguelRua

I had this problem: How to do unit test with a "socket.io-client" if you don't know how long the server take to respond?.

我遇到了这个问题:如果您不知道服务器需要多长时间来响应,如何使用“socket.io-client”进行单元测试?。

I've solved so using mochaand chai:

我已经使用mochachai解决了这个问题:

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);
    });
});