即使呼叫计数为1,Sinon间谍断言也会失败

时间:2017-05-19 19:06:08

标签: node.js unit-testing sinon

使用Node,Sinon,Chai,proxyetch for fetch和Mocha

为什么这个sinon间谍断言fooCallback1.should.have.been.called;未能被调用一次?我在源代码中看到console.log(fooCallback1)callCount为1.

这是第一个也是唯一一个测试...所以我没有看到重置间谍的理由。

function setLight(...args) {
  var request;
  var lightNumber;
  var alertName;
  var callback1;
  var callback2;
  var callback3;

  [request, lightNumber, alertName,
    callback1, callback2, callback3] = args;

  return fetch(request)
    .then(status)
    .then(toJSON)
    .then(() => {
      if(Boolean(callback1)) {
        console.log('one')
        callback1(lightNumber);
        console.log(callback1);
      }
  before(()=> {
    fetch = sinon.stub().returnsPromise();

    var response = {
      status: 200,
      json: () => { 'foo' }
    };

    fetch.resolves(response);

    fetchHelper = proxy('../lib/fetch-helper', {'node-fetch': fetch});

  });

  it('should run fetch for light effect and shutoff', (done)=> {
    var fooCallback1 = sinon.spy();
    fetchHelper.setLight('foo', 123, 'foo-alert', fooCallback1);
    fetch.should.have.been.called;
    fooCallback1.should.have.been.called;        
    done();
  });
  1) when executing setLight should run fetch for light effect and shutoff:
     AssertionError: expected spy to have been called at least once, but it was never called
      at Context.it (test/fetch-helper.js:24:34)





  when executing setLight
    1) should run fetch for light effect and shutoff
one
{ [Function: proxy]
  isSinonProxy: true,
  formatters: 
   { c: [Function: c],
     n: [Function: n],
     D: [Function: D],
     C: [Function: C],
     t: [Function: t],
     '*': [Function: *] },
  reset: [Function: reset],
  invoke: [Function: invoke],
  named: [Function: named],
  getCall: [Function: getCall],
  getCalls: [Function: getCalls],
  calledBefore: [Function: calledBefore],
  calledAfter: [Function: calledAfter],
  calledImmediatelyBefore: [Function: calledImmediatelyBefore],
  calledImmediatelyAfter: [Function: calledImmediatelyAfter],
  withArgs: [Function: withArgs],
  matches: [Function: matches],
  printf: [Function: printf],
  calledOn: [Function],
  alwaysCalledOn: [Function],
  calledWith: [Function],
  calledWithMatch: [Function],
  alwaysCalledWith: [Function],
  alwaysCalledWithMatch: [Function],
  calledWithExactly: [Function],
  alwaysCalledWithExactly: [Function],
  neverCalledWith: [Function],
  neverCalledWithMatch: [Function],
  threw: [Function],
  alwaysThrew: [Function],
  returned: [Function],
  alwaysReturned: [Function],
  calledWithNew: [Function],
  alwaysCalledWithNew: [Function],
  callArg: [Function],
  callArgWith: [Function],
  callArgOn: [Function],
  callArgOnWith: [Function],
  yield: [Function],
  invokeCallback: [Function],
  yieldOn: [Function],
  yieldTo: [Function],
  yieldToOn: [Function],
  spyCall: { [Function: createSpyCall] toString: [Function: toString] },
  called: true,
  notCalled: false,
  calledOnce: true,
  calledTwice: false,
  calledThrice: false,
  callCount: 1,
  firstCall: 
   { proxy: [Circular],
     thisValue: undefined,
     args: [ 123 ],
     returnValue: undefined,
     exception: undefined,
     callId: 11,
     errorWithCallStack: 
      Error
          at Function.invoke (/home/one/github/lifx-weather/node_modules/sinon/lib/sinon/spy.js:205:19)
          at proxy (/home/one/github/lifx-weather/node_modules/sinon/lib/sinon/spy.js:97:22)
          at fetch.then.then.then (/home/one/github/lifx-weather/lib/fetch-helper.js:52:9)
          at process._tickCallback (internal/process/next_tick.js:103:7) },
  secondCall: null,
  thirdCall: null,
  lastCall: 
   { proxy: [Circular],
     thisValue: undefined,
     args: [ 123 ],
     returnValue: undefined,
     exception: undefined,
     callId: 11,
     errorWithCallStack: 
      Error
          at Function.invoke (/home/one/github/lifx-weather/node_modules/sinon/lib/sinon/spy.js:205:19)
          at proxy (/home/one/github/lifx-weather/node_modules/sinon/lib/sinon/spy.js:97:22)
          at fetch.then.then.then (/home/one/github/lifx-weather/lib/fetch-helper.js:52:9)
          at process._tickCallback (internal/process/next_tick.js:103:7) },
  args: [ [ 123 ] ],
  returnValues: [ undefined ],
  thisValues: [ undefined ],
  exceptions: [ undefined ],
  callIds: [ 11 ],
  errorsWithCallStack: 
   [ Error
         at Function.invoke (/home/one/github/lifx-weather/node_modules/sinon/lib/sinon/spy.js:205:19)
         at proxy (/home/one/github/lifx-weather/node_modules/sinon/lib/sinon/spy.js:97:22)
         at fetch.then.then.then (/home/one/github/lifx-weather/lib/fetch-helper.js:52:9)
         at process._tickCallback (internal/process/next_tick.js:103:7) ],
  displayName: 'spy',
  toString: [Function: toString],
  instantiateFake: [Function: create],
  id: 'spy#11' }
    - should set normal alert lock on
    - should set normal alert lock off after time


  0 passing (15ms)
  2 pending
  1 failing

  1) when executing setLight should run fetch for light effect and shutoff:
     AssertionError: expected spy to have been called at least once, but it was never called
      at Context.it (test/fetch-helper.js:27:34)

这是我编写测试的另一种方式,但结果仍然相同:

  before((done)=> {
    fetch = sinon.stub().returnsPromise();
    fetchHelper = proxy('../lib/fetch-helper', {'node-fetch': fetch});
    done()
  });

  after(()=> {
  });

  it('should run fetch for light effect and shutoff', (done)=> {
    function fooCallback1() { console.log('aaaaaaaaaaaaaaaaaaaa') }
    var foo = sinon.spy(fooCallback1)
    fetchHelper.setLight('foo', 123, 'foo-alert', fooCallback1);
    var response = {
      status: 200,
      json: () => { 'foo' }
    };
    fetch.resolves(response);
    fetch.should.have.been.called;
    foo.should.have.been.called;
    done();
  });
  when executing setLight
one
aaaaaaaaaaaaaaaaaaaa
    1) should run fetch for light effect and shutoff
    - should set normal alert lock on
    - should set normal alert lock off after time


  0 passing (10ms)
  2 pending
  1 failing

  1) when executing setLight should run fetch for light effect and shutoff:
     AssertionError: expected fooCallback1 to have been called at least once, but it was never called
      at Context.it (test/fetch-helper.js:28:25

2 个答案:

答案 0 :(得分:3)

fetchHelper.setLight()是异步的,但您的测试并不等待它完成,因此代码的运行顺序如下:

  • fetchHelper.setLight()
  • fetch(request)
  • fetch.should.have.been.called
  • fooCallback1.should.have.been.called
  • done()
  • console.log('one')
  • callback1(lightNumber)
  • console.log(callback1)

您需要重写测试,以便等待回调被调用。你没有使用间谍,而是使用常规回调函数来测试断言:

it('should run fetch for light effect and shutoff', done => {
  fetchHelper.setLight('foo', 123, 'foo-alert', () => {
    fetch.should.have.been.called;
    done();
  });
});

答案 1 :(得分:0)

除了robertklep的优秀答案之外,这是我开始工作的另一种方式。我在调用fetch包装器setLight后调用fetch resolve:

  it('should run fetch for light effect and shutoff', (done)=> {
    var foo = sinon.spy()
    fetchHelper.setLight('foo', 123, 'foo-alert', foo); 
    fetch.resolves(response);
    fetch.should.have.been.called;
    foo.should.have.been.called;
    done();
  });