问题
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:
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, } }); ... });
Reset each one explicitly between tests with mockClear:
describe('handleOnChange', () => { beforeEach(() => { mockOnBlur.mockClear(); mockOnChange.mockClear(); }); ... });
Reset all mocks explicitly between tests with clearAllMocks:
describe('handleOnChange', () => { beforeEach(() => { jest.clearAllMocks(); }); ... });
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