How to test the setter of a useImmer react hook with jest?

74 Views Asked by At

I have a parent component where a piece of state is defined with useImmer like this:

const [example, setExample] = useImmer({
submitterId: {
    value: EMPTY_STRING,
    label: 'Submitter ID',
    disabled: false,
    status: null,
    schema: submitterIdSchema
  },
  submitterName: {
    value: EMPTY_STRING,
    label: 'Submitter Name',
    disabled: false,
    status: null,
    schema: submitterNameSchema
  }});

These states are passed down as props to a child component which I am trying to write jest tests for. This is a simplified example.

export function ChildComponent({
 example,
 setExample,
}) {

  const handleInputChange = async (e, fieldName) => {
    const valueToUpdateWith = e.target.value;
    const schema = example[fieldName].yupSchema;
    const status = await validate(valueToUpdateWith, schema);
   setExample((draft) => {
      draft[fieldName].status = status;
      draft[fieldName].value = valueToUpdateWith;
    });
  };

  return (
    <div id="exampleContainer">
      <ConnectedFieldGenerator // this just loops through keys in example and generates label/input field groups for them
        fieldKeys={Object.keys(example)}
        handleInputChange={handleInputChange}
      />
    </div>
  );
}

I have written the below test in order to cover the handleInputChange function, but in my coverage report the logic inside of setExample is notcovered.

const props = {
  example: EXAMPLE_INITIAL_FIELD_STATE,
  setExample: jest.fn(),
};

    describe('Search Table Renders', () => {
      it('input change of example', async () => {
        const { container } = render(<ChildComponent{...props} />);
        await enterInputValue(container, '#submitterId-input', 'Submitter ID');
        expect(props.setBasicSearchState).toHaveBeenCalled();
      });
    });

How would I cover the below lines in handleInputChange with my tests?

setExample((draft) => {
  draft[fieldName].status = status; // NOT COVERED
  draft[fieldName].value = valueToUpdateWith; // NOT COVERED
});
1

There are 1 best solutions below

0
On BEST ANSWER

You don't need to mock the setExample function, create a parent component for testing purposes. Keep it as simple as possible, retaining enough logic for a single test case. Then, pass the real setExample function returned by the useImmer hook to the <ChildComponent/> component. Finally, assert the latest state the component has rendered.

E.g.

Child.jsx:

import React from 'react';

export function ChildComponent({ example, setExample }) {
  const handleInputChange = async (e, fieldName) => {
    const valueToUpdateWith = e.target.value;
    const status = 'ok';
    setExample((draft) => {
      draft[fieldName].status = status;
      draft[fieldName].value = valueToUpdateWith;
    });
  };

  return (
    <div id="exampleContainer">
      <input type="text" onChange={(e) => handleInputChange(e, 'submitterId')} />
      <div>example.submitterId: {example.submitterId.value}</div>
      <div>example.submitterId.status: {example.submitterId.status}</div>
    </div>
  );
}

Child.test.jsx:

import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { ChildComponent } from './Child';
import { useImmer } from 'use-immer';

describe('ChildComponent', () => {
  test('should pass', () => {
    // Component for testing purposes, keep it simple.
    const ParentComp = () => {
      const [example, setExample] = useImmer({
        submitterId: {
          value: '',
          label: 'Submitter ID',
          status: null,
        },
        submitterName: {
          value: '',
          label: 'Submitter Name',
          status: null,
        },
      });

      return <ChildComponent example={example} setExample={setExample} />;
    };
    render(<ParentComp />);
    const textbox = screen.getByRole('textbox');
    fireEvent.change(textbox, { target: { value: 'test' } });
    expect(screen.getByText('example.submitterId: test')).toBeInTheDocument();
  });
});

Test result:

 PASS  stackoverflow/77582125/Child.test.tsx                                                                                                                                                                                
  ChildComponent
    √ should pass (48 ms)                                                                                                                                                                                                   
                                                                                                                                                                                                                            
-----------|---------|----------|---------|---------|-------------------                                                                                                                                                    
File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s                                                                                                                                                     
-----------|---------|----------|---------|---------|-------------------
All files  |     100 |      100 |     100 |     100 | 
 Child.tsx |     100 |      100 |     100 |     100 | 
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.736 s
Ran all test suites related to changed files.