首页 > 解决方案 > 如何使用 sinon.useFakeTimers 在 javascript 单元测试中设置超时

问题描述

我正在尝试测试以下方法:

endSocketConn(socket) {
    if(!socket) {
        return;
    }

    socket.setTimeout(5000, () => {
        socket.destroy();
    });

    socket.end('commandToSend');
}

我需要测试的场景是如果超过 5 秒,那么 socket.destroy() 必须被调用一次

describe('Testing API | socketPool methods', async function () {

    beforeEach(async function () {
        let clock = sinon.useFakeTimers();
        let socketMock = {
            setTimeout: sinon.spy(),
            destroy: sinon.spy(),
            end: sinon.spy(), 
        };
    });

    afterEach(async function () {
        clock && clock.restore();
    });
    
    describe('Testing endSocketConn() function', function () {
        
        it('It should execute socket.destroy if timeout occurred after sending commandToSend', async function () {
            let result;
            let error = false;
            try {
                clock.tick(6000);
                result = await file.endSocketConn(socketMock);
            } catch (err) {
                error = err;
            }
            expect(error).to.be.false;
            sinon.assert.callCount(socketMock.setTimeout, 1);
            sinon.assert.callCount(socketMock.end, 1);
            
            sinon.assert.callCount(socketMock.destroy, 1); 
            // AssertError: expected spy to be called once but was called 0 times
        });
    });

知道为什么没有正确模拟超时吗?

标签: javascriptunit-testingmocha.jschaisinon

解决方案


你不需要使用sinon.useFakeTimers(),因为我们可以使用存根来实现一个与真实无关的方法setTimeout

例如

file.js

  endSocketConn(socket) {
    if (!socket) {
      return;
    }

    socket.setTimeout(5000, () => {
      socket.destroy();
    });

    socket.end('commandToSend');
  },
};

export { file };

file.test.js

import sinon from 'sinon';
import { file } from './file';

describe('Testing API | EslSocketPoolHandler methods', function () {
  let socketMock;
  beforeEach(function () {
    socketMock = {
      setTimeout: sinon.stub(),
      destroy: sinon.spy(),
      end: sinon.spy(),
    };
  });

  describe('Testing endSocketConn() function', function () {
    it('It should execute socket.destroy if timeout occurred after sending commandToSend', function () {
      socketMock.setTimeout.callsFake((ms, cb) => {
        cb();
      });
      file.endSocketConn(socketMock);

      sinon.assert.callCount(socketMock.setTimeout, 1);
      sinon.assert.callCount(socketMock.end, 1);
      sinon.assert.callCount(socketMock.destroy, 1);
    });
  });
});

单元测试结果:

  Testing API | EslSocketPoolHandler methods
    Testing endSocketConn() function
      ✓ It should execute socket.destroy if timeout occurred after sending commandToSend


  1 passing (7ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |   85.71 |       50 |     100 |   85.71 |                   
 file.ts  |   85.71 |       50 |     100 |   85.71 | 4                 
----------|---------|----------|---------|---------|-------------------

推荐阅读