2

I couldn't find an appropriate answer to this anywhere. I hope it's not a duplicate. I have a simple enzyme test script to simulate form submission that I wrote using Reactjs in ES6 syntax. I am using mocha as my test runner. When I try to simulate the 'submit' of form, the onSubmit function is not being called. My js file that I'm testing:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
    constructor() {
        super();
        this.onSubmitDetails = this.onSubmitDetails.bind(this);
    };

    onSubmitDetails(event) {
        event.preventDefault();
        alert("Hey! Form submitted??")
    }
  render() {
    return (
      <div className="App">
          <form onSubmit={this.onSubmitDetails}>
              <input type="text" className="loginBox" placeholder="Username"
                 required="required"/>
              <input type="password" className="loginBox"  placeholder="Password" required="required"/>
              <input type="submit" className="loginBox submit" value="SIGN IN"/>
          </form>
      </div>
    );
  }
}

export default App;

My test code:

import 'jsdom-global/register';
import React from 'react';
import ReactDOM from 'react-dom';
import { shallow, mount } from "enzyme";
import expect from 'expect';
import sinon from 'sinon';
import App from './App';

describe("Component: LoginContainer", () => {
it('should login', () => {
    const props = {
        fields: {
            user: {
                username: {},
                password: {}
            }
        },
        onSubmitDetails: () => {}
    };
    const onSubmitDetails = sinon.spy();
    const wrapper = mount(<LoginContainer />);

    const username = wrapper.find('.loginBox').get(0);
    username.value = 'abc';
    expect(username.value).toEqual('abc');

    const password = wrapper.find('.loginBox').get(1);
    password.value = 'xyz';
    expect(password.value).toEqual('xyz');

    wrapper.find('form').simulate('submit', { preventDefault(){} });
    console.log(onSubmitDetails.called);  //gives false
    });
});
morghulis
  • 145
  • 11

2 Answers2

1

In the code you pasted the form is in the App component but your test is mounting a LoginContainer. Let's assume it's a typo and your test is mounting App indeed. :)

You are creating a spy but you need to pass it as a prop to your component, and then call that prop in your onSubmitDetails internal method. You almost had it with your props defined in your test, but forgot to pass them to your component.

Depends on what do you want to test: that submitting the form calls a function provided to your component from the outside (via prop)? Then accept that as a prop and call that function in your code:

class App extends Component {
    constructor() {
        super();
        this.onSubmitDetails = this.onSubmitDetails.bind(this);
    };

    onSubmitDetails(event) {
        event.preventDefault();
        //this prop is what you will fake in your test
        this.props.login()
    }
  render() {
    return (
      <div className="App">
          <form onSubmit={this.onSubmitDetails}>
             ...
          </form>
      </div>
    );
  }
}

Then in your test, supply your spy as the login prop:

it('should login', () => {
    //the login prop spy
    const loginSpy = sinon.spy()
    const props = {
        fields: {
            user: {
                username: {},
                password: {}
            }
        },
        login: loginSpy
    };
    const onSubmitDetails = sinon.spy();

    //your code is in App component but you were mouting a LoginContainer??
    const wrapper = mount(<App { ...props } />);

    const username = wrapper.find('.loginBox').get(0);
    //these two lines are non sense
    username.value = 'abc';
    expect(username.value).toEqual('abc');

    const password = wrapper.find('.loginBox').get(1);
    // the following two as well
    password.value = 'xyz';
    expect(password.value).toEqual('xyz');

    // this is where you test your submit event will be calling
    // whatever functions is passed as `login` prop
    wrapper.find('form').simulate('submit', { preventDefault(){} });
    console.log(loginSpy.called);  
    });
});
CharlieBrown
  • 4,143
  • 23
  • 24
0

Since you are trying to stub method inside the component you are trying to test, the above stub won't work. Try this it could help. I am asumming that App and LoginComponent are same.

    import 'jsdom-global/register';
    import React from 'react';
    import ReactDOM from 'react-dom';
    import { shallow, mount } from "enzyme";
    import expect from 'expect';
    import sinon from 'sinon';
    import App from './App';

    describe("Component: LoginContainer", () => {
        const props = {
            fields: {
                user: {
                    username: {},
                    password: {}
                }
            },
        };
        const wrapper = mount(<App />);
        before(() => {
          sinon.stub(App.prototype, 'onSubmitDetails'); // this will stub the method inside app component
        })

        it('should login', () => {



        const username = wrapper.find('.loginBox').get(0);
        username.value = 'abc';
        expect(username.value).toEqual('abc');

        const password = wrapper.find('.loginBox').get(1);
        password.value = 'xyz';
        expect(password.value).toEqual('xyz');

        wrapper.find('form').simulate('submit', { preventDefault(){} });
          expect(App.prototype.onSubmitDetails).to.have.property('callCount', 1); //test your expectation here
        });

        after(() => {
          App.prototype.onSubmitDetails.restore(); // restores stubbed method
       });
    });
duwalanise
  • 1,312
  • 1
  • 14
  • 24
  • In my actual code, my
    is in a component, say which is a child component of . Like: ...
    . How do I access the onSubmitDetails that is now inside and not ?
    – morghulis Feb 06 '17 at 05:09
  • The concept is if we are testing some component and need to spy on the functions inside that components like componentWillMount, componentDidMount or other methods inside that components then we should use the above approach. If we are passing functions as props to the component that we are testing then we can create spy like `const onSubmitDetails = sinon.spy();` and pass it to the component. Looking into your question, onSubmitDetails is inside the component so you can try the above approach or pass function as props in the formComponent from App and test as suggested by @charlie – duwalanise Feb 07 '17 at 04:05