Testing onChange function in Jest

前端 未结 5 2039
悲哀的现实
悲哀的现实 2021-02-01 04:07

I\'m relatively new to Jest and testing in general. I have a component with an input element:

import * as React from \"react\";

export interface inputProps{
            


        
相关标签:
5条回答
  • 2021-02-01 04:12

    How about this one? I simulate the change event using enzyme and perform a snapshot test. Component

    import React, { FunctionComponent, useState } from 'react';
    
    const Index: FunctionComponent = () => {
    
      const [val, setVal] = useState('');
    
      const onInputChange = e => {
        e.preventDefault();
        setVal(e.target.value);
      };
    
      return (
        <input type='text' onChange={onInputChange} value={val} />
      );
    };
    
    export default Index;
    

    Unit Test

    describe('Index with enzyme', () => {
      it('Should set value to state when input is changed', () => {
        const container = shallow(<Index />);
        const input = container.find('input');
        input.simulate('change', { preventDefault: jest.fn, target: { value: "foo" } });
        expect(container).toMatchSnapshot();
      });
    });
    

    Snapshot

    exports[`Index with enzyme Should set value to state when input is changed 1`] = `
      <input
        onChange={[Function]}
        type="text"
        value="foo"
      />
    `;
    
    0 讨论(0)
  • 2021-02-01 04:24

    For those testing using TypeScript (and borrowing from the answers above), you'll need to perform a type coercion (as React.ChangeEvent<HTMLInputElement>) to ensure that the linter can view the signature as being compatible:

    React file

    export class InputBox extends React.Component<inputProps, searchState> {
      onSearch(event: React.ChangeEvent<HTMLInputElement>){
        event.preventDefault();
        //the actual onclick event is in another Component
        this.props.onSearch(event.target.value.trim());
      }
    
      render() {
        return (
          <input
            onChange={this.onSearch} //need to test this
            className={this.props.className} 
            type="text"
            value={this.props.value}
            placeholder={this.props.placeholder} />
          );
      }
    }
    

    Test file

    it('should call onChange prop', () => {
      const onSearchMock = jest.fn();
      const event = {
        target: { value: 'the-value' }
      } as React.ChangeEvent<HTMLInputElement>;
      const component = enzyme.shallow(<InputBox onSearch={onSearchMock} />);
      component.find('input').simulate('change', event);
      expect(onSearchMock).toBeCalledWith('the-value');
    });
    

    or alternatively

    it('should call onChange prop', () => {
      const onSearchMock = jest.fn();
      const event = {
        target: { value: 'the-value' }
      } as React.ChangeEvent<HTMLInputElement>;
      const component = enzyme.mount<InputBox>(<InputBox onSearch={onSearchMock} />);
      const instance = component.instance();
      instance.onSearch(event);
      expect(onSearchMock).toBeCalledWith('the-value');
    });
    
    0 讨论(0)
  • 2021-02-01 04:27

    I struggled with this for hours. Plus since I had multiple select fields on one page. What I found is that Textfield solution works differently from Select.test given on docs.

    On the code I defined SelectProps with id. (You can also go with data-testid)

    I could only trigger dropdown by clicking this field.

    <TextField
      select
      variant = "outlined"
      value = { input.value || Number(0) }
      onChange = { value => input.onChange(value) }
      error = { Boolean(meta.touched && meta.error) }
      open = { open }
      SelectProps = {
        {
          id: `${input.name}-select`,
          MenuProps: {
            anchorOrigin: {
              vertical: "bottom",
              horizontal: "left"
            },
            transformOrigin: {
              vertical: "top",
              horizontal: "left"
            },
            getContentAnchorEl: null
          }
        }
      } 
      { ...props} >
    
      //yourOptions Goes here
    
     </TextField>

    And in my test.

    const pickUpAddress = document.getElementById("address-select");
    
    UserEvent.click(pickUpAddress);
    UserEvent.click(screen.getByTestId("address-select-option-0"));

    Worked like a charm afterwards. Hope this helps.

    0 讨论(0)
  • 2021-02-01 04:35

    I figured out the solution.

    So, instead of passing in the value inside InputBox, we have to pass it inside the second param of simulate as shown below. Then we simply check for equality against the first arg of the first call to the mockFn. Also, we can get rid of the event.preventDefault();

    it("onChange param is the same value as the input element's value property", () => {
        const mockFn = jest.fn();
        const input = enzyme.shallow(<InputBox 
                                        value=""
                                        placeholder="" 
                                        className="" 
                                        onSearch={mockFn}/>);
    
        input.find('input').simulate('change', {target: {value: 'matched'} });
        expect(mockFn.mock.calls[0][0]).toBe('matched');
    });
    
    0 讨论(0)
  • 2021-02-01 04:36

    Syntax on your code snippet I think should be:

    import React from 'react';
    
    export default class InputBox extends React.Component {
      onSearch(event) {
        event.preventDefault();
        this.props.onSearch(event.target.value.trim());
      }
      render () { return (<input onChange={this.onSearch.bind(this)} />); }
    }
    

    The test is failing because, as same you define the preventDefault function on the event object, you also must define other properties used on the onSearch function.

    it('should call onChange prop', () => {
      const onSearchMock = jest.fn();
      const event = {
        preventDefault() {},
        target: { value: 'the-value' }
      };
      const component = enzyme.shallow(<InputBox onSearch={onSearchMock} />);
      component.find('input').simulate('change', event);
      expect(onSearchMock).toBeCalledWith('the-value');
    });
    

    Previous test code needs to define the event shape because you are using shallow rendering. If you want instead to test that the actual input value is being used on your onSearch function you need to try a full render with enzyme.mount:

    it('should call onChange prop with input value', () => {
      const onSearchMock = jest.fn();
      const component = enzyme.mount(<InputBox onSearch={onSearchMock} value="custom value" />);
      component.find('input').simulate('change');
      expect(onSearchMock).toBeCalledWith('custom value');
    });
    
    0 讨论(0)
提交回复
热议问题