Set Immediate() to rescue - testing components with async componentDidMount()

Set Immediate() to rescue - testing components with async componentDidMount()

unit-testing react components using Sinon

Scenario

we had a React class-based components on an older project and while implementing the feature change, we ended up calling an API from ComponentDidMount(). This made this function to be async since it involves a network call.

Sample code

 async componentDidMount() {

    const response = await CommonService.sampleapi(baseApiUrl);
}

Problem

so started testing the component using sinon, mocked the API and rendered the component using shallow and await keywords.

But when I tried to assert the mock call count and state it was always failing. Though I can verify the mocks are working properly using logs, the assert statements that verify the mock CallCount was always 0.

I was searching for something similar to waitFor(), and I ended up using wrapper.instance().update() for updating the state of the component -> but still it didn't help.

 CommonService.sampleapi
      .returns(Promise.resolve( true ));

    let wrapper=await Enzyme.shallow(<componentexample {...defaultProps} />);
    ;
   wrapper.instance().update()
    expect(CommonService.sampleapi.callCount).to.equal(1);

Solution

The function that helped me to resolve the issue is assert the calls inside setImmediate() callbacks.

 CommonService.sampleapi
      .returns(Promise.resolve( true ));

    let wrapper=await Enzyme.shallow(<componentexample {...defaultProps} />);
    ;

setImmediate(()=>
    expect(CommonService.sampleapi.callCount).to.equal(1);
);

setImmediate - if you want to queue the function behind whatever I/O event callbacks that are already in the event queue. 0

Testing a component that runs an async function from componentDidMount (or useEffect) must await a re-render before running assertions. wrapper. update is used to sync Enzyme's component tree before running assertions but doesn't wait for promises or force a re-render.

One approach is to use setImmediate, which runs its callback at the end of the event loop, allowing promises to resolve non-invasively.

References link1 link2