I've looked at various suggestions to solve testing a class property with no success and was wondering if anyone could possibly cast a little more light on where I may be going wrong, here are the tests I've tried all with the error Expected mock function to have been called, but it was not called.
Search.jsx
import React, { Component } from 'react'
import { func } from 'prop-types'
import Input from './Input'
import Button from './Button'
class SearchForm extends Component {
static propTypes = {
toggleAlert: func.isRequired
}
constructor() {
super()
this.state = {
searchTerm: ''
}
this.handleSubmit = this.handleSubmit.bind(this)
}
handleSubmit = () => {
const { searchTerm } = this.state
const { toggleAlert } = this.props
if (searchTerm === 'mocky') {
toggleAlert({
alertType: 'success',
alertMessage: 'Success!!!'
})
this.setState({
searchTerm: ''
})
} else {
toggleAlert({
alertType: 'error',
alertMessage: 'Error!!!'
})
}
}
handleChange = ({ target: { value } }) => {
this.setState({
searchTerm: value
})
}
render() {
const { searchTerm } = this.state
const btnDisabled = (searchTerm.length === 0) === true
return (
<div className="well search-form soft push--bottom">
<ul className="form-fields list-inline">
<li className="flush">
<Input
id="search"
name="search"
type="text"
placeholder="Enter a search term..."
className="text-input"
value={searchTerm}
onChange={this.handleChange}
/>
<div className="feedback push-half--right" />
</li>
<li className="push-half--left">
<Button className="btn btn--positive" disabled={btnDisabled} onClick={this.handleSubmit}>
Search
</Button>
</li>
</ul>
</div>
)
}
}
export default SearchForm
First option:
it('should call handleSubmit function on submit', () => {
const wrapper = shallow(<Search toggleAlert={jest.fn()} />)
const spy = jest.spyOn(wrapper.instance(), 'handleSubmit')
wrapper.instance().forceUpdate()
wrapper.find('.btn').simulate('click')
expect(spy).toHaveBeenCalled()
spy.mockClear()
})
Second option:
it('should call handleSubmit function on submit', () => {
const wrapper = shallow(<Search toggleAlert={jest.fn()} />)
wrapper.instance().handleSubmit = jest.fn()
wrapper.update()
wrapper.find('.btn').simulate('click')
expect(wrapper.instance().handleSubmit).toHaveBeenCalled()
})
I get that with a class property the function is an instance of the class requiring the component to be updated in order to register the function, it looks however like the component handleSubmit function gets called instead of the mock?
Swapping out handleSubmit to be a class function as a method gives me access on the class prototype which passes the test when spying on Search.prototype but I'd really like to get a solution to the class property approach.
All suggestions and recommendations would be grateful!
I suppose the unit test should be robust enough to catch the error
, if case of any undesirable code changes.
Please include strict assertions in your tests.
For the conditional statements, please cover the branches as well. E.g in case of if
and else
statement you will have to write two
tests.
For user actions, you should try to simulate the actions rather than calling the function manually.
Please see the example below,
import React from 'react';
import { shallow } from 'enzyme';
import { SearchForm } from 'components/Search';
describe('Search Component', () => {
let wrapper;
const toggleAlert = jest.fn();
const handleChange = jest.fn();
const successAlert = {
alertType: 'success',
alertMessage: 'Success!!!'
}
const errorAlert = {
alertType: 'error',
alertMessage: 'Error!!!'
}
beforeEach(() => {
wrapper = shallow(<SearchForm toggleAlert={toggleAlert} />);
});
it('"handleSubmit" to have been called with "mocky"', () => {
expect(toggleAlert).not.toHaveBeenCalled();
expect(handleChange).not.toHaveBeenCalled();
wrapper.find('Input').simulate('change', { target: { value: 'mocky' } });
expect(handleChange).toHaveBeenCalledTimes(1);
expect(wrapper.state().searchTerm).toBe('mocky');
wrapper.find('Button').simulate('click');
expect(toggleAlert).toHaveBeenCalledTimes(1);
expect(toggleAlert).toHaveBeenCalledWith(successAlert);
expect(wrapper.state().searchTerm).toBe('');
});
it('"handleSubmit" to have been called with "other than mocky"', () => {
expect(toggleAlert).not.toHaveBeenCalled();
expect(handleChange).not.toHaveBeenCalled();
wrapper.find('Input').simulate('change', { target: { value: 'Hello' } });
expect(handleChange).toHaveBeenCalledTimes(1);
expect(wrapper.state().searchTerm).toBe('Hello');
wrapper.find('Button').simulate('click');
expect(toggleAlert).toHaveBeenCalledTimes(1);
expect(toggleAlert).toHaveBeenCalledWith(errorAlert);
expect(wrapper.state().searchTerm).toBe('Hello');
});
});
So I've managed to create a working solution by first of all updating the wrapper instance and then updating the wrapper. Test now works.
Working test looks like:
it('should call handleSubmit function on submit', () => {
const wrapper = shallow(<Search toggleAlert={jest.fn()} />)
wrapper.instance().handleSubmit = jest.fn()
wrapper.instance().forceUpdate()
wrapper.update()
wrapper.find('.btn').simulate('click')
expect(wrapper.instance().handleSubmit).toHaveBeenCalled()
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With