Why does the ordering of it functions matter in this jest test?

只谈情不闲聊 提交于 2021-02-11 14:50:20

问题


I have the following component...

export default class TextInput extends PureComponent<TextInputProps> {

  private handleOnChange = (event: OnChangeEvent): void => {
    if (!this.props.disabled && this.props.onChange) {
      this.props.onChange(event)
    }
  }

  private handleOnBlur = (event: OnBlurEvent): void => {
    if (!this.props.disabled && this.props.onBlur) {
      this.props.onBlur(event)
    }
  }

  public render(): ReactNode {
    return (
      <Styled.TextInput
        id={this.props.id}
        type={this.props.type}
        onChange={this.handleOnChange}
        onBlur={this.handleOnBlur}
        disabled={this.props.disabled}
      />
    )
  }
}

And am trying to test the handleOnChange function using the following test...


const mockOnChange = jest.fn((): void => { })
const mockOnBlur = jest.fn((): void => { })

const minHandlerProps ={
  id: 'test',
  type: 'text',
  onChange: mockOnChange,
  onBlur: mockOnBlur,
}

describe('handleOnChange', () => {
  it('Should not call the onChange prop when it\'s been passed and TextInput is disabled', () => {
    const wrapper = shallow(<TextInput {...minHandlerProps} disabled={true} />)
    const instance = wrapper.instance()

    instance.handleOnChange()
    expect(minHandlerProps.onChange).not.toHaveBeenCalled()
  })

  it('Should call the onChange prop when it\'s been passed and TextInput is not disabled', () => {
    const wrapper = shallow(<TextInput {...minHandlerProps} />)
    const instance = wrapper.instance()

    instance.handleOnChange()
    expect(minHandlerProps.onChange).toHaveBeenCalled()
  })
})

The tests pass when they are in this order, but if I swap the order around the should not call the onChange prop test fails.

Is this because in this instance, the onChange prop has already been called in the first it()?

Am I supposed to write a separate describe for this?

I've console logged that the props are passing correctly and it looks as though they are, so I'm at a loss as to this behaviour. Thanks anyone who can shed some light on it.


回答1:


By defining the mock functions outside the describe block, you share a single instance of each between all tests. This means you see calls from other tests, so your test are order-dependent (a very bad thing for unit tests).

There are various solutions to this:

  1. Create a new instance for each test, e.g. in a beforeEach:

    describe('handleOnChange', () => {
      let minHandlerProps;
      let mockOnBlur;
      let mockOnChange;
    
      beforeEach(() => {
        mockOnChange = jest.fn((): void => { })
        mockOnBlur = jest.fn((): void => { })
    
        minHandlerProps = {
          id: 'test',
          type: 'text',
          onChange: mockOnChange,
          onBlur: mockOnBlur,
        }
      });
    
      ...
    });
    
  2. Reset each one explicitly between tests with mockClear:

    describe('handleOnChange', () => {       
      beforeEach(() => {
        mockOnBlur.mockClear();
        mockOnChange.mockClear();
      });
    
      ...
    });
    
  3. Reset all mocks explicitly between tests with clearAllMocks:

    describe('handleOnChange', () => {       
      beforeEach(() => {
        jest.clearAllMocks();
      });
    
      ...
    });
    
  4. Get Jest to reset all mocks between tests, by setting clearMocks to true in your configuration.


As a side note, I would not recommend testing that invoking handleOnChange on the instance invokes the mocked function prop. That's the implementation - the behaviour of the component is that the callback is invoked when the Styled.TextInput changes, or better yet when some interaction with that component occurs. Simulating these events leaves you less coupled to your current implementation.



来源:https://stackoverflow.com/questions/61439851/why-does-the-ordering-of-it-functions-matter-in-this-jest-test

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!