Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make Jest wait for all asynchronous code to finish execution before expecting an assertion

I am writing an integration test for for a React application, i.e. a test that tests many components together, and I want to mock any calls to external services.

The issue is that the test seems to execute before the async callback is executed causing my tests to fail.

Is there anyway around this? Can I somehow wait for call async code to finish?

Here is some bad pseudo code to illustrate my point.

I would like to test that when I mount Parent, its Child component render the content that came back from an external service, which i will mock.

class Parent extends component {      render ()      {          <div>             <Child />          </div>      } } class Child extends component {      DoStuff()      {          aThingThatReturnsAPromise().then((result) => {            Store.Result = result          })      }      render()     {        DoStuff()        return(<div>{Store.Result}</div>)       } } function aThingThatReturnsAPromise() {      return new Promise(resolve =>{           eternalService.doSomething(function callback(result) {                resolve(result)           }     }  } 

When I do this in my test, it fails because the It gets executed before the callback gets fired.

jest.mock('eternalService', () => {     return jest.fn(() => {         return { doSomething: jest.fn((cb) => cb('fakeReturnValue');     }); });  describe('When rendering Parent', () => {     var parent;      beforeAll(() => {         parent = mount(<Parent />)     });      it('should display Child with response of the service', () => {         expect(parent.html()).toMatch('fakeReturnValue')     }); }); 

How do I test this? I understand angular resolves this with zonejs, is there an equivalent approach in React?

like image 895
Dan Avatar asked Jun 24 '17 21:06

Dan


People also ask

How do I make Jest test wait?

Instead of putting the test in a function with an empty argument, use a single argument called done . Jest will wait until the done callback is called before finishing the test.

How do I set Jest timeout?

Use jest. setTimeout(newTimeout) to increase the timeout value, if this is a long-running test." 1)Increased jest. setTimeout(30000) from 30000 to 60000.

How do I stop async calls?

You can cancel an asynchronous operation after a period of time by using the CancellationTokenSource. CancelAfter method if you don't want to wait for the operation to finish.


2 Answers

Updated for Jest 27+

For jest 27+, you can also use process.nextTick:

await new Promise(process.nextTick); 

(Thanks to Adrian Godong in the comments)

Original Answer

Here's a snippet that waits until pending Promises are resolved:

const flushPromises = () => new Promise(setImmediate); 

Note that setImmediate is a non-standard feature (and is not expected to become standard). But if it's sufficient for your test environment, should be a good solution. Its description:

This method is used to break up long running operations and run a callback function immediately after the browser has completed other operations such as events and display updates.

Here's roughly how to use it using async/await:

it('is an example using flushPromises', async () => {     const wrapper = mount(<App/>);     await flushPromises();     wrapper.update(); // In my experience, Enzyme didn't always facilitate component updates based on state changes resulting from Promises -- hence this forced re-render      // make assertions  }); 

I used this a lot in this project if you want some working real-world examples.

like image 194
Tadas Antanavicius Avatar answered Sep 23 '22 17:09

Tadas Antanavicius


I would suggest you export aThingThatReturnsAPromise() from its module or file and then import it into your test case.

Since aThingThatReturnsAPromise() returns a promise, you can make use of the asynchronous testing features of Jest. Jest will wait for your promise to resolve and then you can make your assertions.

describe('When rendering Parent', () => {     var parent;      beforeAll(() => {         parent = mount(<Parent />)     });      it('should display Child with response of the service', () => {         expect.assertions(1);          return aThingThatReturnsAPromise().then( () => {             expect(parent.html()).toMatch('fakeReturnValue');         });     }); }); 

For more info, read how Jest handles test cases with Promises in the Jest Docs here

like image 36
Saurabh Misra Avatar answered Sep 22 '22 17:09

Saurabh Misra