如何多次断言存根提取

时间:2017-04-29 01:17:12

标签: javascript node.js unit-testing sinon proxyquire

使用proxyquire,sinon和mocha。

我能够在第一次调用fetch时获取存根。但是在第二次提取调用中,这是递归的,我无法断言它。从输出看,断言可能在测试结束之前运行。在断言后,您将在second fetch控制台中看到此信息。

index.js

var fetch = require('node-fetch');

function a() {
  console.log('function a runs');  
  fetch('https://www.google.com')
    .then((e) => {
      console.log('first fetch');
      b();
    })
    .catch((e)=> {
      console.log('error')
    });
}

function b() {
  fetch('https://www.google.com')
    .then((e) => {
      console.log('second fetch');
    })
    .catch((e)=> {
      console.log('error')
    });
}
a()

测试:

describe('fetch test demo', ()=> {

  it('fetch should of called twice', (done)=> {

    fetchStub = sinon.stub();
    fetchStub2 = sinon.stub();
    fetch = sinon.stub();


    fetchStub.returns(Promise.resolve('hello'));
    fetchStub2.returns(Promise.resolve('hi'));

    var promises = [ fetchStub, fetchStub2 ]

    fetch.returns(Promise.all(promises));

    proxy('../index', {
        'node-fetch': fetch
      });

    fetch.should.have.been.callCount(2);
    done()
  });

});
   fetch test demo
function a runs
    1) fetch should of called twice
first fetch
second fetch

  lifx alert test
    - fetch should of called three times
    when rain change is over 50%
      - should run fetch twice


  0 passing (78ms)
  2 pending
  1 failing

  1) fetch test demo fetch should of called twice:
     expected stub to have been called exactly twice, but it was called once
    stub(https://www.google.com) => [Promise] {  } at a (/home/one/github/lifx-weather/foobar.js:5:3)
  AssertionError: expected stub to have been called exactly twice, but it was called once
      stub(https://www.google.com) => [Promise] {  } at a (foobar.js:5:3)
      at Context.it (test/bar.js:22:28)

2 个答案:

答案 0 :(得分:3)

更新版本

@dman,因为你更新了你的测试用例,我欠你一个更新的答案。尽管如此,这种情况仍然是非正统的 - 看起来你似乎想要忽略“重力法则”,即使你知道它就在你面前。

我会尝试尽可能具有描述性。您有两个功能是按设计执行异步内容。 a()按顺序调用 b() - 这不是recursion。两种功能都不会在完成/失败时通知其呼叫者,即它们被视为即发即弃

现在,让我们来看看你的测试场景。您创建3个存根。其中两个解析为一个字符串,另一个使用Promise.all()组合它们的执行。接下来,您代理'node-fetch'模块

proxy('./updated', {
    'node-fetch': fetch
});

使用存根返回存根1和1的组合执行。 2.现在,如果在任一函数中打印出 fetch 的已解析值,您将看到它是一个存根数组而不是字符串。

function a () {
    console.log('function a runs');
    fetch('http://localhost')
        .then((e) => {
            console.log('first fetch', e);
            b();
        })
        .catch((e) => {
            console.log('error');
        });
}

我猜这不是预期的输出。但是,让我们继续前进,因为这不会杀死你的测试。接下来,您已将断言与 done()语句一起添加。

fetch.should.have.been.callCount(2);
done();

这里的问题是,无论您是否使用完成(),效果都将完全相同。您正在同步模式下执行您的方案。当然在这种情况下,断言总是会失败。但重要的是要了解为什么

因此,让我们重写您的场景,以模仿您要验证的行为的异步特性。

'use strict';

const chai = require('chai');
const sinon = require('sinon');
const SinonChai = require('sinon-chai');
chai.use(SinonChai);
chai.should();

const proxy = require('proxyquire');

describe('fetch test demo', () => {

    it('fetch should of called twice', (done) => {

        var fetchStub = sinon.stub();
        var fetchStub2 = sinon.stub();
        var fetch = sinon.stub();

        fetchStub.returns(Promise.resolve('hello'));
        fetchStub2.returns(Promise.resolve('hi'));

        var promises = [fetchStub, fetchStub2];

        fetch.returns(Promise.all(promises));

        proxy('./updated', {
            'node-fetch': fetch
        });

        setTimeout(() => {
            fetch.should.have.been.callCount(2);
            done();
        }, 10);

    });

});

正如您所看到的,唯一的变化是将断言包装在计时器块中。没什么 - 只要等待10毫秒然后断言。现在测试按预期通过。为什么呢?

嗯,对我来说这很简单。您希望测试2个顺序执行的异步函数,并仍然以同步模式运行断言。这听起来很酷,但它不会发生:)所以你有两个选择:

  • 让您的功能在完成后通知来电者,然后以真正的异步模式运行您的断言
  • 使用非正统技术模仿事物的异步性

基于原始测试场景的回复

  

可以做到。我已经重新考虑了你提供的文件了   可以执行。

     

index.js

const fetch = require('node-fetch');
const sendAlert = require('./alerts').sendAlert;

module.exports.init = function () {
  return new Promise((resolve, reject) => {

      fetch('https://localhost')
          .then(function () {
              sendAlert().then(() => {
                  resolve();
              }).catch(
                  e => reject(e)
              );
          })
          .catch(e => {
              reject(e);
          });

  });
};
     

alerts.js

const fetch = require('node-fetch');

module.exports.sendAlert = function () {
  return new Promise((resolve, reject) => {

      fetch('https://localhost')
          .then(function () {
              resolve();
          }).catch((e) => {
          reject(e);
      });

  });
};
     

test.js

'use strict';

const chai = require('chai');
const sinon = require('sinon');
const SinonChai = require('sinon-chai');
chai.use(SinonChai);
chai.should();

const proxy = require('proxyquire');

describe.only('lifx alert test', () => {

  it('fetch should of called twice', (done) => {

      var body = {
          'hourly': {
              data: [{
                  time: 1493413200,
                  icon: 'clear-day',
                  precipIntensity: 0,
                  precipProbability: 0,
                  ozone: 297.17
              }]
          }
      };

      var response = {
          json: () => {
              return body;
          }
      };

      const fetchStub = sinon.stub();

      fetchStub.returns(Promise.resolve(response));
      fetchStub['@global'] = true;

      var stubs = {
          'node-fetch': fetchStub
      };

      const p1 = proxy('./index', stubs);

      p1.init().then(() => {

          try {
              fetchStub.should.have.been.calledTwice;
              done();
          } catch (e) {
              done(e);
          }
      }).catch((e) => done(e));

  });

});
     

你要做的事情虽然有点不正统   良好的单元测试实践。虽然 proxyquire 支持此功能   通过名为全局覆盖的功能进行存根的模式,它是   explained here为什么在下山之前应该三思而后行   这条路。

     

为了让你的例子通过测试,你只需要添加一个   Sinon存根的额外属性称为 @global 并将其设置为   真正。此标志将覆盖 require()缓存机制   无论调用哪个模块,都使用提供的存根。

     

所以,虽然你所要求的可以做,但我必须同意   评论您的问题的用户,这不应该是   采用作为构建测试的正确方法。

答案 1 :(得分:0)

我找到了另一种方法来完成任务。 可能对某人有用。

    describe('Parent', () => {
      let array: any = [];
      before(async () => {
        array = await someAsyncDataFetchFunction();
        asyncTests();
      });
      it('Dummy test to run before()',async () => {
        expect(0).to.equal(0); // You can use this test to getting confirm whether data fetch is completed or not.
      });
      function asyncTests() {
        array.forEach((currentValue: any) => {
            describe('Child', async () => {
                it('Test '+ currentValue ,() => {
                    expect(currentValue).to.equal(true);
                  })
              })
          });
       }
   });

这就是我在数组的每个元素上实现断言的方式。 (正在异步获取数组数据)。