kevin man
kevin man

Reputation: 297

How to unit test components with react hooks?

Currently trying to unit test components with hooks (useState and useEffects). As I have read, lifecycles can only be tested with a mount and not a shallow render.

Implementation code:

export function MainPageRouter({issuerDeals, match, dbLoadIssuerDeals}) {
console.log("MainPageRouter")
const [isLoading, setIsLoading] = useState(true);


const selectedIssuerId = match.params.id;
const issuerDeal = filterIssuerDealByIssuerId(issuerDeals, 
selectedIssuerId);

useEffect(() => {
   dbLoadIssuerDeals(selectedIssuerId)
     .then(() => {
        setIsLoading(false);
      })
     .catch(function (error) {
        setIsLoading(false);
        });
  }, [selectedIssuerId]);

  if (isLoading) {
    return <MainPageLoading />
  } else if(issuerDeal.length > 0) {
    return <MappedDeal match={match}/>
  } else {
    return <MapDeal match={match}/>
  }

}

const mapStateToProps = state => {
  return {
    deals: state.deals,
    issuerDeals: state.issuerDeals
  }
};

const mapDispatchToProps = {
  dbLoadIssuerDeals
}

export default connect(mapStateToProps, mapDispatchToProps)(MainPageRouter);

However doing so results in this error:

Warning: An update to MainPageRouter inside a test was not wrapped in 
act(...).

When testing, code that causes React state updates should be wrapped into 
act(...):

act(() => {
  /* fire events that update state */
});

Test:

it('Should render Mapped Deal', () => {
    const dbLoadIssuerDeals = jest.fn(() => Promise.resolve({
        payload:{ deals: { dealid: "1", sourceCode: "TEST" }, issuerId: "1" } }))
    const props = createProps(issuerDeals, dbLoadIssuerDeals);
    const mainPageRouter = mount(<MemoryRouter><MainPageRouter{...props} /></MemoryRouter>);
});

Is there a clean way to test that mainPageRouter would return back MappedDeal or MapDeal? I also understand that using mount is more towards integration tests.

Upvotes: 15

Views: 1076

Answers (1)

Sarah Dayan
Sarah Dayan

Reputation: 468

The warning you're getting isn't caused by using hooks per see but because you have a side-effect that causes a state update.

Something is happening after your component's initial render: you're fetching data from your dbLoadIssuerDeals service and updating the local state, resulting in a re-render. However, your test runs right after the first render, meaning it couldn't properly assert anything happening after the effect. You could basically only test that MainPageLoading is displayed, but none of the other branching statements. React's act testing API is warning you about that.

Running your code in act guarantees the execution of state updates and enqueued side-effects. In other words, it lets you "wait" for changes resulting from state updates. You can read more about act in React's official docs.

I recommend using React Testing Library instead of Enzyme or the test utilities of React DOM directly. It wraps updates and events in act for you, allowing you to write more expressive tests without the boilerplate.

Upvotes: 1

Related Questions