React Test Renderer Simulating Clicks on Elements

2020-08-09 08:57发布

问题:

I'm testing a React component using Jest v16.0.1, react-test-renderer v15.4.0 and react-addons-test-utils v15.4.0.

The component has rendered a button:

<button
    type="button"
    className="btn btn-lg btn-primary btn-danger"
    disabled={this.state.cancelButtonDisabled}
    onClick={() => this.handleCancel()}
    ref="cancelButton"
>Cancel</button>);

And in my test I'm rendering the component like so:

const component = renderer.create(
    <MyComponent />
);

const instance = component.getInstance();
// This works but is ugly
component.toJSON().children[1].children[0].props.onClick();
// This doesn't work
ReactTestUtils.Simulate.click(instance.refs.cancelButton);

let tree = component.toJSON();
expect(tree).toMatchSnapshot();

What is the recommended way to simulate a click on this button? You can traverse the JSON representation of the component but it seems like their should be a better way.

Before when I was using ReactTestUtils.renderIntoDocument you could pass in a reference to the component using refs to ReactTestUtils.Simulate.click

I've seen this question - How to interact with components rendered by ReactTestRenderer / Jest but I assume the API has changed as my component instance has no find() method.

回答1:

I have found a solution. Since you are using react, I assume that the onClick handler function is passed to the button as a part of the props. So you can access it through button's props.

component.root.findByType('button').props.onClick();

Or if you have more than one button, you can do this:

component.root.findByProps({className="btn btn-lg btn-primary btn-danger"}).props.onClick();


回答2:

Maybe it's too late but find is an API from enzyme. The answer on the question you referred to assumed enzyme is used as mentioned on the comment.

Something like this should work.

MyComponent.jsx

import React from 'react';

class MyComponent extends React.Component {
  constructor() {
    super();
    this.state = {
      cancelButtonDisabled: false,
    };
  }
  handleCancel() {
    this.props.cancelAction();
  }
  render() {
    return (
      <button
        type="button"
        className="btn btn-lg btn-primary btn-danger"
        disabled={this.state.cancelButtonDisabled}
        onClick={() => this.handleCancel()}
        ref="cancelButton"
      >
        Cancel
      </button>
    );
  }
}

export default MyComponent;

MyComponent.test.jsx

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

describe('Test MyComponent', () => {
  it('should be able to click the button', () => {
    const mockFunction = jest.fn();
    const element = mount(<MyComponent cancelAction={mockFunction} />);
    element.find('button').simulate('click');
    expect(mockFunction).toHaveBeenCalled();
  });
});

without enzyme, it would look like this.

MyComponentWithoutEnzyme.test.jsx

import React from 'react';
import ReactDOM from 'react-dom';
import ReactTestUtils from 'react-addons-test-utils';
import MyComponent from './MyComponent';

describe('Test MyComponent', () => {
  it('should be able to click the button', () => {
    const mockFunction = jest.fn();
    const element = ReactTestUtils.renderIntoDocument(
      <MyComponent cancelAction={mockFunction} />,
    );
    const button = ReactDOM.findDOMNode(element);
    ReactTestUtils.Simulate.click(button);
    expect(mockFunction).toHaveBeenCalled();
  });
});