最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - sinon spy is not called in a stub with async function - Stack Overflow

programmeradmin3浏览0评论

Using sinon and enzyme I wanna test the following component:

// Apple.js
class Apple extends Component {

  componentDidMount = () => {

    this.props.start();
    Api.get()
      .then(data => {
        console.log(data); // THIS IS ALWAYS CALLED
        this.props.end();
      });
  }

  render () {
    return (<div></div>);
  }
}

If I just check endApy.called, it's always false. But wrapping it in a setTimeout will make it pass. Why console.log() is always called but not the props.end? Why setTimeout fixes it? Is there a better way of doing this?

// Apple.test.js
import sinon from 'sinon';
import { mount } from 'enzyme';
import Api from './Api';
import Apple from './Apple';


test('should call "end" if Api.get is successfull', t => {
  t.plan(2);
    sinon
        .stub(Api, 'get')
        .returns(Promise.resolve());

    const startSpy = sinon.spy();
    const endApy = sinon.spy();

    mount(<Apple start={ startSpy } end={ endApy } />);

    t.equal(startSpy.called, true);                    // ALWAYS PASSES 
    t.equal(endSpy.called, true);                      // ALWAYS FAILS
    setTimeout(() => t.equal(endApy.called, true));    // ALWAYS PASSES

    Api.get.restore();
});

Using sinon and enzyme I wanna test the following component:

// Apple.js
class Apple extends Component {

  componentDidMount = () => {

    this.props.start();
    Api.get()
      .then(data => {
        console.log(data); // THIS IS ALWAYS CALLED
        this.props.end();
      });
  }

  render () {
    return (<div></div>);
  }
}

If I just check endApy.called, it's always false. But wrapping it in a setTimeout will make it pass. Why console.log() is always called but not the props.end? Why setTimeout fixes it? Is there a better way of doing this?

// Apple.test.js
import sinon from 'sinon';
import { mount } from 'enzyme';
import Api from './Api';
import Apple from './Apple';


test('should call "end" if Api.get is successfull', t => {
  t.plan(2);
    sinon
        .stub(Api, 'get')
        .returns(Promise.resolve());

    const startSpy = sinon.spy();
    const endApy = sinon.spy();

    mount(<Apple start={ startSpy } end={ endApy } />);

    t.equal(startSpy.called, true);                    // ALWAYS PASSES 
    t.equal(endSpy.called, true);                      // ALWAYS FAILS
    setTimeout(() => t.equal(endApy.called, true));    // ALWAYS PASSES

    Api.get.restore();
});
Share Improve this question edited Sep 15, 2017 at 4:35 Sam R. asked Sep 14, 2017 at 19:48 Sam R.Sam R. 16.5k14 gold badges72 silver badges125 bronze badges
Add a comment  | 

3 Answers 3

Reset to default 15

Api.get is async function and it returns a promise, so to emulate async call in test you need to call resolves function not returns:

Causes the stub to return a Promise which resolves to the provided value. When constructing the Promise, sinon uses the Promise.resolve method. You are responsible for providing a polyfill in environments which do not provide Promise.

sinon
  .stub(Api, 'get')
  .resolves('ok');

your console.log(data) always happens because your Promise does resolve, it just does so after the test has finished, which is why the assertion fails.

By wrapping it in a setTimeout you create another event in the loop, which allows your Promise to resolve before the test finishes, meaning that your assertion will now pass.

This is a fairly common problem when unit testing asynchronous code. Often resolved in wrapping the assertions in setImmediate and calling done from the callback of setImmediate.

https://stackoverflow.com/a/43855794/6024903

I ran into a similar problem with a spy and using await Promise.all(spy.returnValues)worked fine for me. This resolves all the promises and afterwards I can check spy.called.

发布评论

评论列表(0)

  1. 暂无评论