I am trying to perform unit test for my react app using karma/jasmine. The test is to check whether the state has changed or not. I have gone through several references but can't seem to work out the problem. Besides, spyOn() and sinon.spy() are giving erroneous output.
expect(received).toBe(expected)
Expected value to be (using ===):
true
Received:
false
test-snippet
it('should call handleUserIdChange', () => {
const value = '00000000';
const mountedComponentHandle = mount(<LoginForm />);
const onChangeUserID = sinon.spy(mountedComponentHandle.instance(), 'handleUserIdChange');
mountedComponentHandle.update();
(mountedComponentHandle.find('ValidatedInput').at(0)).simulate('change', value);
expect(
onChangeUserID.calledOnce
).toBe(true);
});
Login.js for which tests are written
class LoginForm extends Component {
constructor(props) {
super(props);
this.state = {
userId : '',
password : '',
loginType : ['Customer', 'Payer', 'Super-User'],
userType : 'Customer',
isLoggedIn : false,
loginResponse : '',
FieldsRegister : [],
}
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 });
}
handlePasswordChange(value) {
this.setState({ password : value });
}
....
render() {
return (
<form className="container form-horizontal" onSubmit={this.handleSubmitForm}>
<div className=" pb-10">
<h2 className="text-center">Login</h2>
</div>
<div className='column text-center'>
<span>{this.state.loginResponse}</span>
</div>
<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]{4,10}$/}
validationError={'This is not valid user Id'}
isRequired={true}
/>
....
ValidateInput.js
class ValidatedInput extends Component {
constructor(props) {
super(props);
this.state = {
validations: this.props.validations,
validationError: this.props.validationError
};
this.handleChangeValue = this.handleChangeValue.bind(this);
this.isValid = this.isValid.bind(this);
this.validateInput = this.validateInput.bind(this);
}
handleChangeValue(e) {
this.props.onChange(e.target.value);
var isValidField = this.isValid(e.target);
}
....
render () {
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>
);
}
Any help regarding how to check whether state.userId
has changed or not and suggestions on how to get the test to pass?
Thanks
I am trying to perform unit test for my react app using karma/jasmine. The test is to check whether the state has changed or not. I have gone through several references but can't seem to work out the problem. Besides, spyOn() and sinon.spy() are giving erroneous output.
expect(received).toBe(expected)
Expected value to be (using ===):
true
Received:
false
test-snippet
it('should call handleUserIdChange', () => {
const value = '00000000';
const mountedComponentHandle = mount(<LoginForm />);
const onChangeUserID = sinon.spy(mountedComponentHandle.instance(), 'handleUserIdChange');
mountedComponentHandle.update();
(mountedComponentHandle.find('ValidatedInput').at(0)).simulate('change', value);
expect(
onChangeUserID.calledOnce
).toBe(true);
});
Login.js for which tests are written
class LoginForm extends Component {
constructor(props) {
super(props);
this.state = {
userId : '',
password : '',
loginType : ['Customer', 'Payer', 'Super-User'],
userType : 'Customer',
isLoggedIn : false,
loginResponse : '',
FieldsRegister : [],
}
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 });
}
handlePasswordChange(value) {
this.setState({ password : value });
}
....
render() {
return (
<form className="container form-horizontal" onSubmit={this.handleSubmitForm}>
<div className=" pb-10">
<h2 className="text-center">Login</h2>
</div>
<div className='column text-center'>
<span>{this.state.loginResponse}</span>
</div>
<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]{4,10}$/}
validationError={'This is not valid user Id'}
isRequired={true}
/>
....
ValidateInput.js
class ValidatedInput extends Component {
constructor(props) {
super(props);
this.state = {
validations: this.props.validations,
validationError: this.props.validationError
};
this.handleChangeValue = this.handleChangeValue.bind(this);
this.isValid = this.isValid.bind(this);
this.validateInput = this.validateInput.bind(this);
}
handleChangeValue(e) {
this.props.onChange(e.target.value);
var isValidField = this.isValid(e.target);
}
....
render () {
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>
);
}
Any help regarding how to check whether state.userId
has changed or not and suggestions on how to get the test to pass?
Thanks
4 Answers
Reset to default 3I found the error and the cause of it. Refer this for explanation.
Updated test-snippet:
it('should call handleUserIdChange', () => {
const value = '00000000';
const mountedComponentHandle = mount(<LoginForm />);
const onChangeUserId = sinon.spy(mountedComponentHandle.instance(), 'handleUserIdChange');
mountedComponentHandle.update();
mountedComponentHandle.find('input').at(0).simulate('change', value);
expect(
onChangeUserId.called
).toBe(true);
});
We need to get native element input
for simulating change in input field.
If worth for someone, I had this issue and I solve it by using "mount" instead "shallow" from enzyme. Once you change it, the simulate "change" will work properly.
So for your tests, it looks like you're trying to see whether or not handleUserIdChange
has been called or not. In which case you need to define handleUserIdChange
as the sinonSpy()
and pass it into the LoginForm
ponent's props.
it('should call handleUserIdChange', () => {
const value = '001251623';
const handleUserIdChangeSpy = sinon.spy();
const mountedComponentHandle = mount(<LoginForm handleUserIdChange={handleUserIdChangeSpy} />);
mountedComponentHandle.find('ValidatedInput').at(0).simulate('change', value)
expect(handleUserIdChangeSpy.calledOnce).toBe(true);
});
As for your question at the bottom, if you want to check and see if the state has updated once called, you can simply mock the input and check the state using .state()
after you call a function or interact with your ponent in a way that should/should not effect the state.
it('should update userId', () => {
const mountedComponentHandle = mount(<LoginForm />);
const value = '001251623';
mountedComponentHandle.find('ValidatedInput').at(0).simulate('change', value)
expect(mountedComponentHandle.state().userId).to.equal(value);
});
Rather then looking for the ponent display name .find('ValidatedInput')
pass in the ponent (constructor) itself .find(ValidatedInput)