I'm using Semantic UI React to render a <Dropdown /> in my component. I want to test that the event handler passed on to the component is called with the value from the dropdown.
<MyComponent
onChange={myEventHandler}
/>
My component:
class MyComponent extends React.Component {
handleChange = (e, {value}) => {
this.props.onChange(value)
}
render() {
return (
<Dropdown
onChange={this.handleChange}
value={this.props.values}
/>
)
}
}
And the test using enzyme:
let wrapper = mount(
<MyComponent
onChange={jest.fn()}
/>
)
it('calls myEventHandler()', () => {
const myEventHandler = jest.fn()
wrapper.setProps({ onChange: myEventHandler})
wrapper.find(Dropdown).simulate('change', { value: ['val'] })
expect(myEventHandler).toHaveBeenCalledWith(['val'])
})
The issue is that the test fails as it is but:
.simulate('change') and expect like so expect(myEventHandler).toHaveBeenCalled();onChange={this.handleChange.bind(this, 'anyOtherVarHereWorks'}.So what am I doing wrong? Would love your help! Thanks!
I鈥檇 suggest not testing Dropdown in the MyComponent tests - all you need to do there is assert that the proper callback is passed to onChange.
Separatey, in the Dropdown tests, you can assert that whatever callback you get passed gets attached to the right onChange prop.
The rest would be testing react itself, and the DOM, which isn鈥檛 really adding value.
You're a wise man, @ljharb, thanks!
Out of curiosity, though, even if I shouldn't test it that way, why does it not work? Am I providing the wrong arguments to simulate?
Also, thinking about it again, surely I would need to test <MyComponent />'s handleChange(), right? If so, in this example, I need to check that myEventHandler() is called with the right value, needn't I?
OK, scrap all that I commented. I've made it work this way:
it('calls myEventHandler()', () => {
const mockMyEventHandler = jest.fn()
wrapper.setProps({ onChange: mockMyEventHandler })
wrapper.find(Dropdown).simulate('change', '', { value: ['val'] })
expect(mockMyEventHandler).toHaveBeenCalledWith(['val'])
})
That works, and it conveniently treats MyComponent as a black box. So yes, I am testing tested things from Dropdown, but in one test I am also testing that:
myEventHandler prop is passed on to Dropdown;Dropdown calls handleChange method;handleChange method does what I want it to do, i.e. calling myEventHandler with the right value.What are your thoughts about that? Am I testing too many things in one test?
Thanks!
I'd do wrapper.find(Dropdown).prop('onChange')({ value: ['val' ] }); instead of using simulate, but otherwise that seems fine.
Why would you not use simulate, @ljharb?
@zdettwiler because it doesn't actually simulate anything - all it does is calls a prop function for you, and provide a synthetic event. You can do that yourself much more clearly without suggesting to future readers that it's a faithful simulation of what happens in the browser.
That's right, simulate is just sugar coating. But isn't that why it was created, to make the code sweeter? Otherwise, it's just a useless function!?
The problem is that the name ("simulate") implies semantics that it simply doesn't have. The reason enzyme has it is because react-test-renderer had it - but in practice, this sugar just isn't useful, and its implicitness obscures what's really going on.
Super helpful, @ljharb, thanks!
I get the event triggered, but the provided option in my event is always undefined:
This is my onChange callback:
private onChangeDropdown = ( event: React.FormEvent
Option is always undefined in my test.
I am doing :
wrapper.findWhere((c) => { return c.hasClass("ms-Dropdown-caretDownWrapper"); }).simulate("change", { target: { value: dropDownOption } });
The callback gets triggered in my test but the passed option is always undefined. I tried serveral ways to include the value, but it just does not get set. Anyone knows why?
My optoins are of type IDropDownOptions with key as integer and text as string.
Most helpful comment
I'd do
wrapper.find(Dropdown).prop('onChange')({ value: ['val' ] });instead of usingsimulate, but otherwise that seems fine.