7

使用 proxyquire、sinon 和 mocha。

我能够在第一次调用 fetch 时存根 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)
4

3 回答 3

3

更新后的版本

@dman,由于您更新了测试用例,我欠您一个更新的答案。虽然改写了,但这个场景仍然是非正统的——在某种意义上,你似乎想忽略“万有引力定律”,即使你知道它就在你面前。

我会尽量做到描述性。您有两个按设计 执行异步操作的功能。a () 顺序调用b() - 顺便说一下,这不是递归。这两个函数在完成/失败时都不会通知它们的调用者,即它们被视为即发即弃

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

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

使用返回存根 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();

这里的问题是,无论你是否使用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 个按顺序执行的异步函数,并且仍然在同步模式下运行您的断言。这听起来很酷,但它不会发生 :) 所以你有 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);
          });

  });
};

警报.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);
      });

  });
};

测试.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通过名为global overrides的功能支持这种存根模式,但 这里解释了为什么任何人在走这条路之前都应该三思而后行。

为了使您的示例通过测试,您只需向 Sinon 存根添加一个名为@global的额外属性并将其设置为 true。此标志覆盖require()缓存机制并使用提供的存根,无论从哪个模块调用。

因此,尽管您的要求可以完成,但我必须同意评论您的问题的用户,但不应将其作为构建测试的正确方法。

于 2017-05-05T13:26:49.010 回答
0

这也是使用Promise.all().

注意:如果使用 fetch 的方法,这将不起作用,json并且您需要在数据的resolve()for 逻辑中传递数据。它只会在解决时传递存根。但是,它将断言调用的次数。

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

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

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

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

    var promises = [ fetchStub, fetchStub2 ]
    var promise  = Promise.all(promises);

    fetch.returns(promise);

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

    return promise.then(() => {
      fetch.should.have.callCount(2);
    });

  });

});
于 2017-05-07T04:53:37.393 回答
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);
                  })
              })
          });
       }
   });

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

于 2019-09-04T12:09:50.050 回答