简体   繁体   中英

Testing React Function was Called with Jest

I have a React class which renders a child component called PersonalDetails. The code works fine, however I cannot get Jest to verify the saveData was called correctly when the Enter key is pressed in the field. I have mocked the saveData function passed to the PersonalDetails but the assertions fail (code condensed for brevity) on the mock:

class Home extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            name: null
        };    
        this.saveData = this.saveData.bind(this);
    }

    render() {        
        return (
            <div>
                <PersonalDetails name={this.state.name} saveData={this.saveData}/>;
                <OtherThing saveData={this.saveData}/>
            </div>
        )
    }

    saveData(key, value){
        var d = {};
        d[key] = value;
        this.setState(d)
    }
}

The PersonalDetails module is as follows:

class PersonalDetails extends React.Component {

    constructor(props) {
        super(props);
        this.handleNameChange = this.handleNameChange.bind(this);
    }

    render() {            
        return (
            <div>
                <input onKeyPress={this.handleNameChange}
                       defaultValue={this.props.name}/>
            </div>
        )
    }

    handleNameChange(event) {
        if (event.nativeEvent.keyCode != 13) return;
        this.props.saveData('name',event.target.value)
    }
}

Jest test case in question:

it("saves data on change", function () {

    var saveData = jest.genMockFunction(); // mocking saveData
    const pdComponent = TestUtils.renderIntoDocument(
        <PersonalDetails saveData={saveData} name="My Name"/>
    );

    var input = TestUtils.findRenderedDOMComponentWithTag(
        pdComponent, 'input'
    );

    var node = ReactDOM.findDOMNode(input);
    node.value = "New Name"; // As per FB tests
    TestUtils.Simulate.change(node, {target: {value: "New Name"}});
    TestUtils.Simulate.keyDown(node, {key: "Enter", keyCode: 13, which: 13});
    expect(node.value).toBe("New Name"); // Passes
    // Fails
    expect(pdComponent.props.saveData).toBeCalledWith("New Name");
    // Also Fails
    expect(saveData).toBeCalledWith("New Name");

});

Any ideas what I'm missing here?

You can create a Spy. A spy would be spying ie keeping track.Using the Spy, you can check if the function was called or not.

Sample Code( Copied from the net)

var CompSpec = {
  displayName: "Comp",

  plop: function() {
    console.log("plop");
  },

  render: function() {
    this.plop();
    return React.DOM.div("foo");
  }
};

var stub = sinon.stub(CompSpec, "plop");
var Comp = React.createClass(CompSpec);
React.addons.TestUtils.renderIntoDocument(Comp());

// plop() is properly stubbed, so you can
sinon.assert.called(stub); // pass

The cause of your problem is simple yet difficult to discover:

it('just an input test', () => {
    const keyPressAction = jest.fn(() => {
        console.log('key press');
    });

    const input = TestUtils.renderIntoDocument(<input type="text" onKeyPress={keyPressAction.bind(this)}/>);

    const inputNode: Element = ReactDOM.findDOMNode(input);

    inputNode.setAttribute('value', 'test');
    TestUtils.Simulate.keyPress(inputNode, {key: "Enter", keyCode: 13, which: 13});

    expect(input.getAttribute('value')).toEqual('test');
    expect(keyPressAction).toBeCalled();
});

Compare this with the solution in your question.

For the input element you use onKeyPress event, but in the test you simulate keyDown which can't work, just replace it with keyPress and the handler will be called.

Have a nice day!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM