reactjsjestjsreact-hooksreact-test-renderer

React test onChange on input using react shallow-renderer


I'm trying to test searchForm component of React with onChange prop.

const SearchForm = () => {
  const [value, setValue] = useState('');

  return (
    <form className={styles.searchForm}>
      <input
        value={value}
        onChange={(e) => setValue(e.target.value)} // test this line
        className={styles.searchForm__input}
      />
      <button type="submit" aria-label="Search" className={styles.searchForm__button} />
    </form>
  );
};

Here is example of my test:

import React from 'react';
import ShallowRenderer from 'react-test-renderer/shallow';
import SearchForm from '../index';

const setUp = () => {
  const renderer = new ShallowRenderer();
  renderer.render(<SearchForm />);
  return renderer.getRenderOutput();
};

describe('render form component', () => {

  it('handle onChange in form input field', () => {
    const result = setUp();
    expect(result).toMatchSnapshot();
  });
});

This test passes, but JEST says that this line of code (with onChange) is uncovered.

I found how to launch onChange:

result.props.children[0].props.onChange();

This launches original prop but i get error on e.target -- cannot read property of undefined. I feel like i need to mock setValue somehow, but i can't figure out how. I'm new to JEST. Maybe this may be done with just react-test-renderer in better way.


Solution

  • Here is the solution:

    index.tsx:

    import React, { useState } from 'react';
    
    export const SearchForm = () => {
      const [value, setValue] = useState('');
    
      return (
        <form>
          <input value={value} onChange={(e) => setValue(e.target.value)} />
          <button type="submit" aria-label="Search" />
        </form>
      );
    };
    

    index.test.tsx:

    import React from 'react';
    import TestRenderer, { act } from 'react-test-renderer';
    import ShallowRenderer from 'react-test-renderer/shallow';
    import { SearchForm } from './';
    
    describe('66907704', () => {
      it('should handle onChange event', () => {
        const testRenderer = TestRenderer.create(<SearchForm />);
        const testInstance = testRenderer.root;
        expect(testInstance.findByType('input').props.value).toEqual('');
        const mEvent = { target: { value: 'teresa teng' } };
        act(() => {
          testInstance.findByType('input').props.onChange(mEvent);
        });
        expect(testInstance.findByType('input').props.value).toEqual('teresa teng');
      });
    
      it('should handle onChange event when use shallow render', () => {
        const shallowRenderer = ShallowRenderer.createRenderer();
        shallowRenderer.render(<SearchForm />);
        let tree = shallowRenderer.getRenderOutput();
        let input = tree.props.children[0];
        const mEvent = { target: { value: 'teresa teng' } };
        input.props.onChange(mEvent);
        tree = shallowRenderer.getRenderOutput();
        input = tree.props.children[0];
        expect(input.props.value).toEqual('teresa teng');
      });
    });
    

    unit test result:

     PASS  examples/66907704/index.test.tsx (6.636 s)
      66907704
        ✓ should handle onChange event (10 ms)
        ✓ should handle onChange event when use shallow render (1 ms)
    
    -----------|---------|----------|---------|---------|-------------------
    File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    -----------|---------|----------|---------|---------|-------------------
    All files  |     100 |      100 |     100 |     100 |                   
     index.tsx |     100 |      100 |     100 |     100 |                   
    -----------|---------|----------|---------|---------|-------------------
    Test Suites: 1 passed, 1 total
    Tests:       2 passed, 2 total
    Snapshots:   0 total
    Time:        7.443 s
    

    package versions:

    "jest": "^26.6.3",
    "react": "^16.14.0",