【问题标题】:How to test async methods using sinon如何使用 sinon 测试异步方法
【发布时间】:2021-02-18 15:12:37
【问题描述】:

我正在对一个函数进行单元测试,该函数在其中调用另一个异步任务而无需等待。原因是我不想等待那个函数执行。

const fn1 = async () => {
  fn2();
  return 'foo';
};

const fn2 = async () => {
  await fn3();
  await fn4();
};

const fn3 = async () => {
  await s3.upload(params).promise();
};

const fn4 = async () => {
  await s3.upload(params).promise();
};

我想对 fn1() 和 fn2() 进行单元测试,这是一个异步任务,这是我尝试过的:

describe("unit test fn1", () => {
  
  let s3Stub;
   
  beforeEach(() => {
     s3Stub = sinon.stub(S3, "upload");
 });

  afterEach(() => {
    s3Stub.restore();
 });
  
  it("fn1 test" , async () => {
     const actualValue = await fn1();
     expect(s3Stub.calledTwice).to.be.true; 
  });

});

理想情况下,s3Stub 应该被调用两次,但只被调用一次,我的测试就完成了。

我不想在 fn2() 上使用 await,因为我希望 fn2() 作为独立线程运行。关于我们如何对此类案例进行单元测试有什么想法吗?

【问题讨论】:

    标签: javascript unit-testing mocha.js sinon sinon-chai


    【解决方案1】:

    用于测试fn1fn2的单元测试解决方案:

    index.js:

    const s3 = require('./s3');
    
    const fn1 = async () => {
      exports.fn2();
      return 'foo';
    };
    
    const fn2 = async () => {
      await exports.fn3();
      await exports.fn4();
    };
    
    const fn3 = async () => {
      await s3.upload(params).promise();
    };
    
    const fn4 = async () => {
      await s3.upload(params).promise();
    };
    
    exports.fn1 = fn1;
    exports.fn2 = fn2;
    exports.fn3 = fn3;
    exports.fn4 = fn4;
    

    s3.js:

    const s3 = {
      upload() {
        return this;
      },
      async promise() {},
    };
    
    module.exports = s3;
    

    index.test.js:

    const fns = require('./');
    const sinon = require('sinon');
    const { expect } = require('chai');
    
    describe('64705245', () => {
      afterEach(() => {
        sinon.restore();
      });
      describe('fn1', () => {
        it('should return foo', async () => {
          const fn2Stub = sinon.stub(fns, 'fn2').resolves();
          const actual = await fns.fn1();
          expect(actual).to.be.equal('foo');
          sinon.assert.calledOnce(fn2Stub);
        });
      });
    
      describe('fn2', () => {
        it('should pass', async () => {
          const fn3Stub = sinon.stub(fns, 'fn3').resolves();
          const fn4Stub = sinon.stub(fns, 'fn4').resolves();
          await fns.fn2();
          sinon.assert.calledOnce(fn3Stub);
          sinon.assert.calledOnce(fn4Stub);
        });
      });
    });
    

    单元测试结果:

      64705245
        fn1
          ✓ should return foo
        fn2
          ✓ should pass
    
    
      2 passing (16ms)
    
    ----------|---------|----------|---------|---------|-------------------
    File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    ----------|---------|----------|---------|---------|-------------------
    All files |   83.33 |      100 |   33.33 |   83.33 |                   
     index.js |   86.67 |      100 |      50 |   86.67 | 14,18             
     s3.js    |   66.67 |      100 |       0 |   66.67 | 3                 
    ----------|---------|----------|---------|---------|-------------------
    

    【讨论】:

    • 嘿@slideshowp2。感谢您的及时回复。我可以清楚地看到方法的不同。我只模拟了外部 s3 依赖项,而您模拟了整个函数。有什么特别的原因吗?模拟函数还可能包含一些业务逻辑。此外,如果我们删除exports.fn2(),那么即使在存根之后fn2() 也不会被模拟。但是使用exports.fn2(),代码会模拟该函数。在函数中使用导出的确切用途是什么?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-08-23
    • 2022-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-08
    • 1970-01-01
    相关资源
    最近更新 更多