7

I used arrow function inside of my React component to avoid binding this context, for example my component look like this;

class Comp extends Component {
   _fn1 = () => {}
   _fn2 = () => {}
   render() {
      return (<div></div>);
   }
}

How do I test _fn1 and _fn2 function in my test cases? Because these kind of function did not associated with React component itself, so when I do

 fnStub = sandbox.stub(Comp.prototype, "_fn1");

it is not going work, since _fn did not bind with Comp.prototype. Thus, how can I test those functions in React if I want to create function with arrow syntax? Thanks!

Leo Hsieh
  • 71
  • 1
  • 2

2 Answers2

4

ES6 functions or arrow functions are not added to the class prototype. However, there are a couple of ways to test them:-

  • Test that the functions themselves are called when a suitable event occurs ES5 functions exist on the class prototype and something like this is possible:

    import Component from 'path/to/component';
    import { shallow } from 'enzyme';
    describe(<Component>, () => {
      it('should call handleSubmit', () => {
        const spy = jest.spyOn(Component.prototype, 'handleSubmit');
        const wrapper = shallow(<Component />);
        ...
        //Invoke handleSubmit
        ...
        expect(spy).toBeCalled()
      });
    });
    

whereas ES6 functions exist on the instance of the mounted component(you can also use shallow)

    import Component from 'path/to/component';
    import { mount } from 'enzyme';
    describe(<Component>, () => {
      it('should call handleSubmit', () => {
        const wrapper = mount(<Component />);
        ...
        const spy = jest.spyOn(wrapper.instance(), 'handleSubmit');
        //update the instance with the new spy
        wrapper.instance().forceUpdate();
        ...
        //invoke handleSubmit
        expect(spy).toBeCalled()
      });
    });
  • Test their functionality by simulating actions that will invoke these functions and test for the expected behavior

Assuming component content such as:

      state = {
        title: 'Current Title'
      };
      updateTitle = (event) => {
        title = event.target.value;
        this.setState({ title });
      }
      render() {
        return (
          <div>
            <input type="text" value={this.state.title} onChange={this.updateTitle} />
          <div>
        )
      }

Test

    ...
    wrapper.find('input').simulate('change', {target: {value: 'New title'}});
    expect(wrapper.state().title).toBe('New Title');
    ...

I hope this helps.

dondrzzy
  • 182
  • 1
  • 7
0

In general I find it easier to test that these functions have resulted in a correct component state, rather than test the function itself. For example, here is a component that toggles a state variable when a button is clicked:

class MyComponent extends Component {
    state = {
        toggle: false
    }

   _fn1 = () => {
       this.setState(previousState => ({
           toggle: !previousState.toggle
       });
   }

   render() {
      const { toggle } = this.state;

      return (
          <button onClick={this.clickHandler}>
              Turn me {toggle ? 'on' : 'off'}
          </button>
      );
   }
}

My preferred approach here would be to test the component as a whole, i.e. the "unit" of the unit test is the component. The test would therefore find the button, simulate a click, and ensure that the correct text is shown. This may not be a textbook unit test, but it achieves the goal of testing the component.

Using sinon/chai/mocha/enzyme:

describe('My Component', () => {
    it('alternates text display when the button is clicked', () => {
        const wrapper = shallow(<MyComponent />);

        expect(wrapper).to.have.text('Turn me off');

        wrapper.find('button').simulate('click');

        expect(wrapper).to.have.text('Turn me on');
    });
});
Alex Young
  • 4,009
  • 1
  • 16
  • 34