How to mock e.preventDefault in react component's child
Unit TestingReactjsmocha.jsSinonEnzymeUnit Testing Problem Overview
Hy, I don't know how to mock an inline function in React component's child
My stack: sinon
, chai
, enzyme
;
Component usage:
<ListItem onClick={() => someFn()} />
Component's render:
render() {
return (
<li>
<a href="#" onClick={e => {
e.preventDefault();
this.props.onClick();
}}
> whatever </a>
</li>
);
}
Here we have onClick
function that calls e.preventDefault()
. How to tell to <a href>
(link
) to not to call e.preventDefault()
? How can I mock an onClick
?
Below is what I have tried in tests:
Shallow copy setup
function setup() {
const someFn = sinon.stub();
const component = shallow(
<ListItem
onClick={() => {
someFn();
}}
/>
);
return {
component: component,
actions: someFn,
link: component.find('a'),
listItem: component.find('li'),
}
}
And the test
it('simulates click events', () => {
const { link, actions } = setup();
link.simulate('click'); //Click on <a href>
expect(actions).to.have.property('callCount', 1); //will be fine if we remove e.preventDefault()
});
Test's output error:
TypeError: Cannot read property 'preventDefault' of undefined
Unit Testing Solutions
Solution 1 - Unit Testing
Try this
link.simulate('click', {
preventDefault: () => {
}
});
Solution 2 - Unit Testing
test('simulates click events', () => {
const e = { stopPropagation: jest.fn() };
const component = shallow(<ListItem{...props} />);
const li = component.find('li').at(0).childAt(0)
li.props().onClick(e)
expect();
});
Solution 3 - Unit Testing
For those using Jest and @testing-library
or react-testing-library
s fireEvent
, you need to provide an initialised event object, otherwise the event can't be dispatched via your element.
One can then assert on e.preventDefault
being called by assigning a property to that initialised event:
test('prevents default on click', () => {
const {getByText} = render(<MyComponent />);
const button = getByText(/click me/);
// initialise an event, and assign your own preventDefault
const clickEvent = new MouseEvent('click');
Object.assign(clickEvent, {preventDefault: jest.fn()});
fireEvent(button, clickEvent);
expect(clickEvent.preventDefault).toHaveBeenCalledTimes(1);
});
Similarly for stopPropagation
.
Anton Karpenko's answer for Jest was useful.
Solution 4 - Unit Testing
Just to note that this is an issue only when using shallow
enzyme renderer. In case of full DOM renderer mount
, the event object contains the preventDefault
method, therefore you don't have to mock it.
Solution 5 - Unit Testing
You can define an object with regarding function you will mock via some testing tool, for example look at Jest and Enzyme
describe('Form component', () => {
test('deos not reload page after submition', () => {
const wrapper = shallow(<TodosForm />)
// an object with some function
const event = { preventDefault: () => {} }
// mocks for this function
jest.spyOn(event, 'preventDefault')
wrapper.find('form').simulate('submit', event)
// how would you know that function is called
expect(event.preventDefault).toBeCalled()
})
})
Solution 6 - Unit Testing
I would suggest to create new object based on jest.fn() with
const event = Object.assign(jest.fn(), {preventDefault: () => {}})
then use it:
element.simulate('click', event);
Solution 7 - Unit Testing
I am using Web Components and this works for me -
const callback = jest.fn();
MouseEvent.prototype.stopPropagation = callback;
const element = createElement({});
element.shadowRoot.querySelector('ul').click();
expect(callback).toHaveBeenCalledTimes(1);