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

mys*_*une 58 unit-testing websocket node.js socket.io

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

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

在下面的示例中,"工作......"永远不会打印出来.

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

    });
});
Run Code Online (Sandbox Code Playgroud)

相反,我只是得到:

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


  1 test complete (26 ms)
Run Code Online (Sandbox Code Playgroud)

有什么建议?

mys*_*une 62

在进一步探讨和刺激之后,我在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();
        });

    });

});
Run Code Online (Sandbox Code Playgroud)

我发现在beforeEach,socket.on('connect'...)监听器中放置done()对于建立连接至关重要.例如,如果你在侦听器中注释掉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();
});
Run Code Online (Sandbox Code Playgroud)

我是Mocha的新手,因此可能有一个非常明显的理由要求在套接字范围内放置done().希望这个小细节可以让我的鞋子中的其他人免于拉毛.

对我来说,上面的测试(正确的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)
Run Code Online (Sandbox Code Playgroud)

  • 在创建新套接字时,将{'forceNew':true}添加到选项中.这样,您就可以在单元测试中创建多个客户端套接字. (3认同)
  • @mysterlune socket.on('connect'...)范围内的done()确保套接字在测试开始运行之前连接.操作排队,直到在上一个上下文中调用done(). (2认同)

hel*_*ont 9

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

});
Run Code Online (Sandbox Code Playgroud)


Ada*_*dam 5

自己处理回调和承诺可能很困难,非平凡的例子很快就会变得非常复杂和难以阅读。

有一个名为socket.io-await-test的工具可以通过 NPM 使用,它允许您在测试中挂起/等待,直到使用 await 关键字触发事件。

  describe("wait for tests", () => {
    it("resolves when a number of events are received", async () => {
        const tester = new SocketTester(client);
        const pongs = tester.on('pong');
        
        client.emit('ping', 1);
        client.emit('ping', 2);
        await pongs.waitForEvents(2) // Blocks until the server emits "pong" twice. 

        assert.equal(pongs.get(0), 2)
        assert.equal(pongs.get(1), 3)
    })
})
Run Code Online (Sandbox Code Playgroud)