Home > Mobile >  How to test a function inside useEffect with Jest and Enzyme?
How to test a function inside useEffect with Jest and Enzyme?

Time:04-23

I am testing my components using jest and enzyme. I want to load my component when the whole page is loaded so I am using load event inside useEffect, here is my code

const RatingsAndReviews = (props: RatingsAndReviewsProps) => {

const [pageLoaded, setPageLoaded] = useState<boolean>(false)

const handleLoad = () => {
    if (document.readyState === 'complete') {
      setTimeout(() => {
        setPageLoaded(true)
      }, 1500)
    }
}

React.useEffect(() => {
    window.addEventListener('load', handleLoad)
    return () => {
      window.removeEventListener('load', handleLoad)
    }
}, [])

return (...some code)
}

I want to test this handleLoad function but I am not able to figure it out how I can pass this mock function in my component as the component already expect props and as it is TypeScript I can't pass anything else other that the required props, here is my test case

it('Should run handleLoad function onMount', ()=>{
    jest.spyOn(React, 'useEffect').mockImplementation(f => f())
    const handleLoad = jest.fn();
    wrapper = mount(<RatingsAndReviews {...propObj} />)
    expect(handleLoad).toHaveBeenCalled();
})

I am getting this error expect(jest.fn()).toHaveBeenCalled()

Expected number of calls: >= 1
Received number of calls:    0

CodePudding user response:

Try not to mock the implementation of the third-party library function, because the incorrect mock implementation will destroy its functionality. For example, useEffect(effect) react hook does not just execute the effect function. Consider,

React defers running useEffect until after the browser has painted.

How do you mock it? Incorrect mocking will lead to unexpected behavior. Your tests may pass based on incorrect mock implementation, but the code will fail when it is actually run. That's why you'd better not mock the third-party library. Of course, this is not absolute. You can mock the functions of the third-party library if they are simple and self-contained.

For the React Component, we should do black-box tests that only test the behavior and functionality of the component, not the implementation. We should treat the component as a unit instead of the function inside it.

We should test what is rendered when the pageLoaded state changes.

The event handlers defined inside the function component are private, you can't access them from the outside(test code). So you can't invoke them directly. Instead, you should trigger them by user event. The load event for your case.

E.g.

index.tsx:

import React from 'react';
import { useEffect, useState } from 'react';

export const RatingsAndReviews = (props) => {
  const [pageLoaded, setPageLoaded] = useState<boolean>(false);

  console.log('pageLoaded: ', pageLoaded);
  const handleLoad = () => {
    if (document.readyState === 'complete') {
      setTimeout(() => {
        setPageLoaded(true);
      }, 1500);
    }
  };

  useEffect(() => {
    window.addEventListener('load', handleLoad);
    return () => {
      window.removeEventListener('load', handleLoad);
    };
  }, []);

  return <div>{pageLoaded ? 'loaded' : 'not loaded'}</div>;
};

index.test.tsx:

import { mount } from 'enzyme';
import React from 'react';
import { act } from 'react-dom/test-utils';
import { RatingsAndReviews } from '.';

describe('RatingsAndReviews', () => {
  it('Should run handleLoad function onMount', () => {
    jest.useFakeTimers();
    const wrapper = mount(<RatingsAndReviews />);
    window.dispatchEvent(new Event('load'));
    expect(wrapper.text()).toBe('not loaded');
    act(() => {
      jest.advanceTimersByTime(1500);
    });
    expect(wrapper.text()).toBe('loaded');
  });
});

Test result:

 PASS  stackoverflow/71953030/index.test.tsx (11.883 s)
  RatingsAndReviews
    ✓ Should run handleLoad function onMount (47 ms)

  console.log
    pageLoaded:  false

      at RatingsAndReviews (stackoverflow/71953030/index.tsx:7:11)

  console.log
    pageLoaded:  true

      at RatingsAndReviews (stackoverflow/71953030/index.tsx:7:11)

-----------|---------|----------|---------|---------|-------------------
File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------|---------|----------|---------|---------|-------------------
All files  |   93.33 |       75 |      80 |   92.86 |                   
 index.tsx |   93.33 |       75 |      80 |   92.86 | 19                
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        13.575 s, estimated 14 s

package versions:

"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.5",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"jest": "^26.6.3",
  • Related