Can't spy on an event handler even though I ca

2019-07-18 05:23发布

问题:

I want to test an event handler in React using Jest/Jasmine/Enzyme.

MyComponent.js:

import React from 'react';
class MyComponent extends React.Component {
    constructor(props) {
        super(props);
        this.clickHandler = this.clickHandler.bind(this);
        this.otherMethod  = this.otherMethod .bind(this);
    }

    clickHandler() { this.otherMethod(); }
    otherMethod() {}

    render() { return <div onClick={this.clickHandler}/>; }
}
export default MyComponent;

MyComponent.test.js:

import React from 'react';
import {mount} from 'enzyme';
import MyComponent from './MyComponent';

it('should work', () => {
    const componentWrapper = mount(<MyComponent/>);
    const component = componentWrapper.get(0);

    spyOn(component, 'otherMethod' ).and.callThrough();
    spyOn(component, 'clickHandler').and.callThrough();

    componentWrapper.find('div').simulate('click');

    expect(component.otherMethod ).toHaveBeenCalled(); // works
    expect(component.clickHandler).toHaveBeenCalled(); // does not work
});

In spite of the fact that I think I'm spying on the two component methods identically, one of them (for otherMethod) works while the other (for clickHandler) does not. I clearly am calling clickHandler as otherMethod wouldn't be called if I wasn't, but toHaveBeenCalled isn't being picked up for clickHandler somehow. Why?

I understand that I don't really have to use either .bind(this) or .and.callThrough() on otherMethod but I use both just to treat the two methods identically and using them on otherMethod shouldn't actually make any difference.

This other SO answer states that I have to spy on a function before attaching it as a listener. If this is my problem then I don't know how to resolve it: The spyOn syntax requires the object that the method is a property of (component in this case) but using component requires prior mounting of MyComponent which forces me to attach the listener first.

It may be relevant that my code uses React (and thus I include reactjs as a question tag) but somehow I doubt it.

回答1:

For this kind of tests, the common path is to call your handler methods on your component instance instead of simulating the event.

You can be sure the onClick prop is test by the React team, what you can check is what happens when "they" call your handler.

This also allows you to use shallow rendering which at least for me is much faster. You can get a reference to the instance easily with the instance() method in your wrapper, and you can then call your handler and spy on whatever you want.

const wrapper = mount(<MyComponent />)
const instance = wrapper.instance()
instance.clickHandler()
//assert whatever

In fact, probably attaching your spies to the instance`s methods (that have already been binded) will do the trick as well. :)

http://airbnb.io/enzyme/docs/api/ReactWrapper/instance.html

Hope it helps!