Funsaized
Funsaized

Reputation: 2130

What should be tested when calling onClick() with a redux action creator?

I'm trying to test a simple checkbox input component that fires an action in it's onChange method to save the value of the checkbox (True or False). The component is below:

import React, {Component} from 'react';
import uuid from 'uuid/v1';
import './styles.css';
import { connect } from 'react-redux';
import { saveCheckboxInput } from '../../actions/userInputActions';

class CheckboxSingle extends Component {

  constructor () {
    super();
    this.onChange = this.onChange.bind(this);
    this.state = {
      id : uuid(), // generate a unique id
    }
  }

  onChange(event) {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    this.props.saveCheckboxInput(this.props.linkId, value, this.props.desc, this.props.relatedLinkIds, this.props.stepNumber);
  }

  render(){
    return(
      <div className="col-sm-12 no-padding-left">
        <label className="checkbox-container label-text">{this.props.desc}
          <input id={this.state.id} type="checkbox" name="checkBoxValue" checked={this.props.isChecked}
      onChange={(e) => this.onChange(e)}/>
          <span className="checkmark"></span>
        </label>
      </div>
    )
  }
}

function mapStateToProps(state, ownProps) {
  // Tie checkBoxValue to store answer
  // Get answers in the context of checkbox (determines if checked or not)
  var stepAnswers = state.userInputState.stepResponses[ownProps.stepNumber];
  var isCheckedValue = null;
  // Note: only functional w/ one checkbox input in flow
  // TODO: make functional for multiple checkbox inputs in flow
  for(var i=0; i < stepAnswers.length; i++) {
    if(stepAnswers[i].type === "questionnaire-checkbox-input") {
      isCheckedValue = stepAnswers[i].value;
    }
  }
  return {
    isChecked : isCheckedValue
  };
}



export default connect(
  mapStateToProps,
  { saveCheckboxInput },
 )(CheckboxSingle);

With the test to simulate the onChange() function below:

describe('CheckboxSingle', () => {

  const initialState = {
    userInputState: {
       stepResponses: [
        {},
        {
          type: "questionnaire-checkbox-input",
          name: "mockLinkId",
          value: false,
          prefixText: "mockDesc",
          relatedLinkIds: ["mock1", "mock2"]
        }
      ]
    }
  }
  const mockStore = configureStore()
  let store, shallowWrapper, dispatch

  beforeEach(() => {
    store = mockStore(initialState)
    dispatch = jest.fn();
    shallowWrapper = shallow(<CheckboxSingle store={store} dispatch={dispatch} desc="mockDesc"
  linkId="mockLinkId" relatedLinkIds={["mock1", "mock2"]} stepNumber={1} />).dive()
  });    

  // TODO: test action creator firing upon click
  test('should call onChange after clicked', () => {
    const onChangeFake = jest.spyOn(shallowWrapper.instance(), 'onChange');
    shallowWrapper.find('input[type="checkbox"]').simulate('change', { target: { checked: true } });
    expect(onChangeFake).toHaveBeenCalledTimes(1);
  });

});

What would be the best way to test that this.props.saveCheckboxInput is fired upon a change to the component (similar to the simulated change test)? New to enzyme so any insight would be appreciated!

Upvotes: 2

Views: 1926

Answers (1)

Olivier Boiss&#233;
Olivier Boiss&#233;

Reputation: 18113

First of all onChange={(e) => this.onChange(e)} is a bad practise, because it will create a new function for each render of the component, you can simply write onChange={this.onChange}

Then to test the prop saveCheckboxInput has been called, you just need to check that the dispatch function of your store has been called with an argument corresponding to the action created by the original saveCheckboxInput function

import { saveCheckboxInput } from '../../actions/userInputActions';

let store, shallowWrapper;

beforeEach(() => {
    store = mockStore(initialState)
    store.dispatch = jest.fn();
    shallowWrapper = shallow(
        <CheckboxSingle 
            store={store} 
            desc="mockDesc"
            linkId="mockLinkId" 
            relatedLinkIds={["mock1", "mock2"]} 
            stepNumber={1} 
        />
    ).dive();
}); 

test('should call onChange after clicked', () => {
    const action = saveCheckboxInput(
        "mockLinkId", 
        true, 
        "mockDesc", 
        ["mock1", "mock2"], 
        1
    );

    shallowWrapper.find('input[type="checkbox"]')
        .simulate('change', { target: { checked: true } });
    expect(store.dispatch).toHaveBeenCalledWith(action);
});

Upvotes: 5

Related Questions