Search code examples
reactjsjestjsenzymereact-hooks

How to write Test cases for useEffect Hook in React using Jest & Enzyme?


import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';


const InputComponent = ({ item, data }) => {
  const [value, setValue] = useState('');
  // Binding values for Edit based on unique Key
  useEffect(() => {
    if (data && data[item.field] && data[item.field] !== 'undefined') {
      setValue(data[item.field]);
    }
  }, [data,item.field]);
  //On change setting state
  const setState = e => {
    setValue(e.target.value);
  };
  return (
    <div className='Input-Containter' data-test='InputBox-tbl-evt'>
      <input
        data-test='input-ele'
        type='text'
        value={value}
        onChange={e => setState(e)}
      />
    </div>
  );
};
InputComponent.propTypes = {
  item: PropTypes.object,
  data: PropTypes.object
};

InputComponent.defaultProps = {
    data: {
        id: '1',
        name: 'd'
    },
    item:{
        field: 'id',
    }
};

export default InputComponent;

Can someone help me How to test for setValue() in useEffect

-> Updated complete code for this Component

-> Component will take some data for binding values into input element & in the same

component we can edit values in it as-well.


Solution

  • First, let's take closer look onto useEffect section. What does it mean?

    1. if any of prop is changed
    2. and combination of new values are given some meaningful value(not undefined)
    3. we initialize input's value based on those props even if we have to override custom value

    How to test that? Changing prop(s) and validating input's value.

    Based on that we may have up to 3(changed only first prop, only second or both) * 2 (if result is undefined or not) * 2 (if there has been already custom value provided and stored in useState or not) = 12. But I believe exhaustive testing is not a good way. So I'd put most checks in single test case:

    it('re-init value for nested input after props changes', () => {
      const wrapper = mount(<InputComponent />);
    
      function getInput(wrapper) {
        return wrapper.find("input").prop("value");
      }
    
      expect(getInput(wrapper).props("value")).toEqual("1"); // based on default props 
      getInput(wrapper).props().onChange({ target: {value: "initial"} }); // imitating manual change
    
      expect(getInput(wrapper).props("value")).toEqual("initial"); 
      wrapper.setProps({data: {a: "some-a", b: "undefined"} });
    
      expect(getInput(wrapper).props("value")).toEqual("initial");
      wrapper.setProps({ item: { field: "c" } }); // data[item.field] is undefined
    
      expect(getInput(wrapper).props("value")).toEqual("initial");
      wrapper.setProps({ item: {field: "b" } }); // data[item.field] is "undefined"
    
      expect(getInput(wrapper).props("value")).toEqual("initial");
      wrapper.setProps({ item: {field: "a" } }); // data[item.field] is meaningful
    
      expect(getInput(wrapper).props("value")).toEqual("some-a");
    });
    

    As for getValue helper - it's needed cause we cannot just memoize input element itself like:

    const wrapper = mount(...);
    const input = wrapper.find("input");
    ...
    expect(input.prop("value")).toEqual();
    ...
    expect(input.prop("value")).toEqual();
    

    Details can be found in Enzyme's docs. Or just know we need to re-run find after any update.

    Also beware Enzyme's setProps does not replace current props but update them by merging(as well as React's setState does with state).