Search code examples
javascriptreactjsunit-testingsinonstub

Testing a component which calls an async function


I have a react component which calls an async function passed in as a prop and then calls another function in the then function.

I've trivialised it below as an illustration.

E.g.

const Form = ({ doSomething, closeModal }) => 
<form onSubmit={(e) => {doSomething().then(() => closeModal())
}}>
...
</form>

I am attempting to test that closeModal was called like so:

it('should doSomething then call closeModal', () => {

  const doSomethingStub = sinon.stub().resolves()
  const closeModalStub = sinon.stub()

  const props = {
    doSomething: doSomethingStub,
    closeModal: closeModalStub
  }

  const wrapper = shallow(<Form {...props}/>)

  wrapper.find(`form`).simulate(`submit`)

  expect(doSomethingStub.called).toEqual(true)
  expect(closeModalStub.called).toEqual(true)

})

In my example, only the first expectation is true. Have I done something wrong with the sinon.stub setup? or what I'm expecting? it feels like something minor but I can't pin it down


Solution

  • You're exactly right, it just needs a minor change:

    then queues a callback for execution. Callbacks execute when the current synchronous code completes and the event loop grabs whatever is queued next.

    The test is running to completion and failing before the callback queued by then from within onSubmit() has a chance to run.

    Give the event loop a chance to cycle so the callback has a chance to execute and that should resolve the issue. That can be done by making your test function asynchronous and awaiting on a resolved promise where you want to pause the test and let any queued callbacks execute:

    it('should doSomething then call closeModal', async () => {
    
      const doSomethingStub = sinon.stub().resolves()
      const closeModalStub = sinon.stub()
    
      const props = {
        doSomething: doSomethingStub,
        closeModal: closeModalStub
      }
    
      const wrapper = shallow(<Form {...props}/>)
    
      wrapper.find(`form`).simulate(`submit`);
    
      // Pause the synchronous test here and let any queued callbacks execute
      await Promise.resolve();
    
      expect(doSomethingStub.called).toEqual(true)
      expect(closeModalStub.called).toEqual(true)
    
    });