ReactJS Unit Testing - TypeError: this.props.onChange is not a function

Anup Raj

I am doing unit-testing on React Components with karma+jasmine involving Login. The tests always throws error

? LoginForm test > ValidatedInput test > should validate password

TypeError: this.props.onChange is not a function

 at ValidatedInput.handleChangeValue (src/components/ValidatedInput.js:14:24)
 at node_modules/enzyme/build/ShallowWrapper.js:844:23
 at ReactDefaultBatchingStrategyTransaction.perform (node_modules/react-test-renderer/lib/shallow/Transaction.js:143:20)
 at Object.batchedUpdates (node_modules/react-test-renderer/lib/shallow/ReactDefaultBatchingStrategy.js:62:26)
 at Object.batchedUpdates (node_modules/react-test-renderer/lib/shallow/ReactUpdates.js:97:27)
 at ReactShallowRenderer.unstable_batchedUpdates (node_modules/react-test-renderer/lib/shallow/ReactShallowRenderer.js:130:25)
 at performBatchedUpdates (node_modules/enzyme/build/ShallowWrapper.js:103:21)
 at node_modules/enzyme/build/ShallowWrapper.js:843:13
 at withSetStateAllowed (node_modules/enzyme/build/Utils.js:284:3)
 at ShallowWrapper.simulate (node_modules/enzyme/build/ShallowWrapper.js:840:42)
 at Object.<anonymous> (src/__tests__/login-test.js:93:37)
 at Promise.resolve.then.el (node_modules/p-map/index.js:42:16)
 at process._tickCallback (internal/process/next_tick.js:109:7)

Following are my files:

LoginForm.js renders the component

<ValidatedInput
    name={'userId'}
    type={'text'}
    title={'User ID'}
    value={this.state.userId}
    placeholder={'Enter User ID'}
    onChange={this.handleUserIdChange}
    onComponentMounted={this.handleRegisterFormFields}
    validations={/^[0-9]{5,10}$/}
    validationError={'This is not valid user Id'}
    isRequired={true}
/>

I have bind the handleUserIdChange in constructor and defined in the class body as

handleUserIdChange(value) {
    this.setState({ userId : value });
}

ValidatedInput.js

import React, { Component } from 'react';

class ValidatedInput extends Component {
    constructor(props) {
        super(props);

        this.handleChangeValue = this.handleChangeValue.bind(this);
        this.isValid = this.isValid.bind(this);
        this.validateInput = this.validateInput.bind(this);
    }

    /**********************************************/
    // referred function //
    handleChangeValue(e) {
        this.props.onChange(e.target.value);
        var isValidField = this.isValid(e.target);
    }
    /**********************************************/    
    isValid(input) {
        if (input.getAttribute('required') !== null && input.value === "") {
            input.classList.add('Error');
            input.nextSibling.textContent = this.props.validationError;
            return false;
        } else {
            input.classList.remove('Error');
            input.nextSibling.textContent =  '';
        }

        if(input.value !== "") {
            if(!this.validateInput(input.value)) {
                input.classList.add('Error');
                input.nextSibling.textContent = this.props.validationError;
                return false;
            } else {
                input.classList.remove('Error');
                input.nextSibling.textContent =  '';
            }
        }
            return true;
    }

    validateInput(value) {
        var regularExpressionToBeMatched = this.props.validations;
        return regularExpressionToBeMatched.test(value);
    }

    componentDidMount() {
        if (this.props.onComponentMounted) {
            this.props.onComponentMounted(this);
        }
    }

    render () {
        console.log(this.props.onChange);
        return (
            <div className="form-group">
                <div className="col-5 text-center">
                    <label htmlFor={this.props.name}>{this.props.title}</label>
                </div>
                <div className="col-5 text-center">
                    <input
                        className="form-input text-center"
                        type={this.props.type}
                        ref={this.props.name}
                        name={this.props.name}
                        value={this.props.value}
                        required={this.props.isRequired}
                        placeholder={this.props.placeholder}
                        onChange={this.handleChangeValue}
                    />
                    <span className='Error'></span>
                </div>
            </div>
        );
    }
}

export default ValidatedInput;

The following is the code of the test:

it('should validate userId', () => {
    const component = shallow( <ValidatedInput
            name={'userId'}
            type={'text'}
            title={'User Id'}
            value={'001251623'}
            placeholder={'Enter User Id'}
            validations={/^[0-9]{5,10}$/}
            validationError={'This is not valid user Id'}
            isRequired={true}
    />);
    const handleChangeValue = spyOn(component.instance(), 'handleChangeValue');
    component.find('input').simulate('change', {target: { value: '00125' } });
    // expect(component.state().value).equals("00125");
    expect(handleChangeValue.calledWith('00125'));
});

I have looked extensively in all resources. The app is working fine and console.log(this.props.onChange) in ValidatedInput says bound handleUserIdChange. Please suggest a work-around or change. Thanks.

EDIT 1

LoginForm.js

class LoginForm extends Component {
    constructor(props) {
        super(props);
        this.state = {
            userId : '',
            password : '',
            loginType : ['Customer', 'Payer', 'Super-User'],
            userLoggedInAs : ['Customer'],
            userType : '',
            FieldsRegister : [],
            serverMessage : '',
        }
        this.handleClearForm = this.handleClearForm.bind(this);
        this.handleSubmitForm = this.handleSubmitForm.bind(this);
        this.handleUserIdChange = this.handleUserIdChange.bind(this);
        this.handlePasswordChange = this.handlePasswordChange.bind(this);
        this.handleUserTypeChangeSelect = this.handleUserTypeChangeSelect.bind(this);
        this.handleRegisterFormFields = this.handleRegisterFormFields.bind(this);
    }

    handleUserIdChange(value) {
        this.setState({ userId : value });
    }
...

EDIT 2

it('should validate userId', () => {
            const component = shallow( <ValidatedInput
                    name={'userId'}
                    type={'text'}
                    title={'User Id'}
                    value={'001251623'}
                    placeholder={'Enter User Id'}
                    validations={/^[0-9]{5,10}$/}
                    validationError={'This is not valid user Id'}
                    isRequired={true}
                    onChange={handleUserIdChange()}
            />);
            const handleChangeValue = spyOn(component.instance(), 'handleChangeValue');
            component.find('input').simulate('change', {target: { value: '00125' } });
            // expect(component.state().value).equals("00125");
            expect(handleChangeValue.calledWith('00125'));
        });
Dekel

In your example the <ValidateInput> component has the onChange prop:

<ValidatedInput
    ...
    onChange={this.handleUserIdChange}

But inside your test it doesn't:

        const component = shallow( <ValidatedInput
                name={'userId'}
                type={'text'}
                title={'User Id'}
                value={'001251623'}
                placeholder={'Enter User Id'}
                validations={/^[0-9]{5,10}$/}
                validationError={'This is not valid user Id'}
                isRequired={true}
                ---> Missing the onChange={..} <---
        />);

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

ReactJS TypeError: this.props. is not a function

TypeError: props.articles.map is not a function in reactjs

Unit testing select element's `onChange` function with Enzyme

How to get value of nested props function for unit testing

ReactJS props.onchange destructuring

ReactJS Error: "Uncaught TypeError: this.props.alarms.forEach is not a function"

Testing onChange function in Jest

TypeError: undefined is not a function when unit-testing custom redux hook

AngularJS: Unit testing controller returns "TypeError: $scope.$watch is not a function"

How to pass onChange and props in a function

Uncaught TypeError: $(...).onChange is not a function

TypeError: setEmail is not a function onChange

Reactjs add onChange to props.children dynamically

Unit Testing Android function

Unit Testing a Function Macro

Unit testing a function with assertions

multiple buttons in one component TypeError: onChange (prop) is not a function (dealing with props) React

Unit testing an function returning a function

reactjs error - onChange is not a function error

TypeError: this.props.function is not a function

Uncaught TypeError: setUserName is not a function at onChange

VueJS - Unit testing with vue-test-utils gives error - TypeError: _vm.$t is not a function

Unit Testing local function in Lua

Angular unit testing a function variable

Golang: Replace function unit testing

unit testing iOS function with dispatch

Unit testing a function that depends on database

Unit testing of a function with conditional statement

Unit testing async function with callback