'Jest Enzyme Cannot spy the property because it is not a function; undefined given instead when spyOn testing a function in component
I am testing on the execution of functions inside a React component that's connected to redux store. I was only able to spy on some of the functions, the rest all returned: Cannot spy the setEmail(or other function names) property because it is not a function; undefined given instead
below in code I added arrows pointing out which ones are spy-able, when I console.log(BaseForm.WrappedComponent.prototype.functionName) the ones that are not spy-able returned undefined. the one that can be spied on returned [Function:...] I really do not understand why?
class BaseForm extends React.Component {
constructor(props) {
super(props);
this.state = {
ages: [],
};
}
componentDidMount() {
this.createAges(); <----- can spy on this one as it is a function
}
setEmail = (elemName, value) => { <------ Cannot spy the setEmail property because it is not a function; undefined
this.setState({ email: value });
this.props.saveEmail(value);
};
handleEmailSignup = (e) => { <-------- Cannot spy the setEmail property because it is not a function; undefined
this.setState({ offersSignup: e.target.checked });
};
onChangeAge = (e) => { <------ Cannot spy the setEmail property because it is not a function; undefined
this.setState({
selectedAge: e.target.value,
selectedAgeIndex: e.target.index - 1,
errorAge: '',
});
};
createAges() { <------ can spyOn as it is a function
let ages = [
{ value: '', text: !__isEmpty(sessionStorage.getItem('gf')) ? 'Kid age' : 'Your age' },
{ value: '14 and younger', text: '14 and younger' },
{ value: '15', text: '15' },
{ value: '16', text: '16' },
{ value: '17', text: '17' },
];
this.setState({ ages: ages });
}
render() {
return (
<div>
<div data-type="email" className="textbox-wrapper">
<Textbox
type="email"
placeholder="Email Address"
name="register-email"
onChange={this.setEmail}
onBlur={this.checkUserByEmail}
defaultValue={this.state.email} <------- in test the value does not change, but on UI it does and functions well
isError={!__isEmpty(this.props.emailErrorClass)}
/>
</div>
<Dropdown options={this.state.ages} onChange={this.onChangeAge} selectedValue={this.state.selectedAge} />
<div>
<input name="offersSignup" type="checkbox" onChange={this.handleEmailSignup} checked={this.state.offersSignup} />
</div>
</div>
);
}
}
const mapDispatchToProps = { saveEmail };
export default connect(null, mapDispatchToProps)(BaseForm);
it.only('set email in local state if onChange of Textbox is fired', () => {
//console.log(BasePartnerRegistrationForm.WrappedComponent.prototype.setEmail);
// above is undefined
const setEmailSpy = jest.spyOn(BaseForm.WrappedComponent.prototype, 'setEmail');
const wrapper = mount(
<Provider store={store}>
<BaseForm {...baseProps} />
</Provider>
);
const event = { target: { value: 'event value' } };
wrapper.find('Textbox').at(0).simulate('change', event);
wrapper.update();
expect(setEmailSpy).toHaveBeenCalled();
// below shows unchanged email value in state
// expect(wrapper.find('Textbox').at(0).props().defaultValue).toBe(event.target.value);
});
Below tried a new approach, console errors: Expected: "event value" Received: "" in other words,per test case, the setState is not functioning, but it functions well on UI and in chrome console too.
it('set email in local state if onChange of Textbox is fired', async () => {
const wrapper = mount(
<Provider store={store}>
<BasePartnerRegistrationForm {...baseProps} />
</Provider>
);
const event = { target: { value: 'event value' } };
wrapper.find('Textbox').at(0).simulate('change', event);
await waitFor(() => expect(wrapper.find('Textbox').at(0).props().defaultValue).toBe(event.target.value));
});
Solution 1:[1]
Rather than spying on the method call, just check the DOM. First, I would suggest you look at React Testing Library for finding fields, setting values, querying the DOM for changes. This, with Jest, is the new standard in React testing (even included with React-Create-App). It's a paradigm shift, in that use test user interaction and result (as a user would), which is testing the underlying logic.
The next thing to consider is timing. When you set state it isn't immediate. It takes a few milliseconds for state changes to render. RTL provides a waitFor method that simplifies this further.
waitFor(() => /*some assertion*/);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Steve -Cutter- Blades |
