testing custom react methods with jest and enzyme - reactjs

I am trying to test a method within a react component. The component is a form and it should be testing the handleSubmit() method gets called when the submit button is clicked. I have tried the below.
it('handlesSubmit when submit button is clicked', () => {
wrapper.find(Button).simulate('click');
expect(wrapper.instance().handleSubmit).toHaveBeenCalled();
})
This gave an error jest.fn() value must be a mock function or spy. So I tried this:
it('handlesSubmit when submit button is clicked', () => {
const handleSubmit = jest.fn();
wrapper.find(Button).simulate('click');
expect(handleSubmit).toHaveBeenCalled();
})
This errors saying Expected mock function to have been called

The first block fails because wrapper.instance().handleSubmit is not a jest mock function; it is whatever the class method defines it as.
The second block fails because handleSubmit, while it is a jest mock function, is not tied to your wrapper component at all. It is a local variable. When you simulate the click, it is again calling the actual implementation.
In order to accomplish what you are trying to do, you have to do something like this
it('handlesSubmit when submit button is clicked', () => {
const handleSubmit = jest.fn();
WrapperComponent.prototype.handleSubmit = handleSubmit;
const wrapper = shallow(<WrapperComponent />);
wrapper.find(Button).simulate('click');
expect(handleSubmit).toHaveBeenCalled();
})
where WrapperComponent is the component you are testing.
The above ought to work, but you can sometimes accomplish something similar in a nicer way. Depending on your component's implementation, it is oftentimes easier to test that the functionality in your handleSubmit method is called rather than the handleSubmit method itself. For instance, if my component was something like
class TestComponent extends React.Component {
constructor(props) {
super(props)
this.state = { clicked: false }
this.onClick = this.onClick.bind(this)
}
onClick() {
this.props.onClick()
this.setState({ clicked: true })
}
render() {
return (
<button onClick={ this.onClick }>
{ 'Click Me' }
</button>
)
}
}
I could test it by doing
it('calls onClick props and sets clicked state to true when clicked', () => {
const onClick = jest.fn();
const testComp = shallow(<TestComponent onClick={ onClick } />);
wrapper.find('button').simulate('click');
expect(onClick).toHaveBeenCalled();
expect(testComp.state('clicked')).toBe(true)
})
I generally prefer this type of test because I don't have to overwrite prototype, and it is actually testing that the click triggers the logic I expect. The original test really only covers that I am passing this.handleSubmit as an onClick prop to a Button component and nothing more.

Related

Trying to test a simulate change on a textinput component using Jest

I'm trying to simulate a change event on a component(ListPage)
<TextInput
className="search-s"
id="textM"
width="m"
type="search"
handleChange={this.props.updateS}
placeholder="Search for a scenario"
/>
</div>
The handleChange attribute calls a prop function called updateS which looks like
updateS(e) {
this.setState({
name: e.target.value,
});
}
And what I currently have for the test function
it("should call handleChange on change with the correct params", () => {
const wrapper = shallow(<ListPage />);
const spy = jest.spyOn(wrapper.instance(), "handleChange");
wrapper.instance().forceUpdate();
const p = wrapper.find(".search-s");
p.simulate("change");
expect(spy).toHaveBeenCalled();
});
For some reason my test function doesnt work when I try to simulate a change and check whether the updateSearch function was called. All the guides online have examples of testing functions within the component but not passed props which I feel is what is causing the problem. Any insight would be great

how to test a method of a component in enzyme is it calls a method which is passed as prop

handleConfirm = () => {
this.props.handleCompletionDateChange();
this.setState({ showInformation: false });
};
I have above method handleConfirm in our class component DateSection
if I test handleConfirm method using below code I am getting error as
TypeError: _this.props.handleCompletionDateChange is not a function
const wrapper1 = shallow(<DateSection {...props} />);
const instance = wrapper1.instance() as any;
spyOn(instance, 'handleConfirm').and.callThrough();
expect(instance.handleConfirm()).toBe(true);
expect(instance.handleConfirm).toHaveBeenCalled();
How should pass the above function as a prop while testing,
You better not access instance() in tests. Tests become fragile(say, converting component into functional will break everything while component itself can be fine) and less reliable(if you calling methods that will never be called in real life - e.g. if that method is not called by others and not bound as event handler).\
Instead you need to find way to call that with the help of .simulate() or .props().someCabllackProp(...).
Assuming this handleConfirm is onClick handled for some <button name="confirm">:
it('calls handleCompletionDateChange', () => {
const handleCompletionDateChange = jest.fn();
const wrapper1 = shallow(<DateSection
{...props}
handleCompletionDateChange={handleCompletionDateChange} \
/>);
wrapper1.find("button[name='confirm']").simulate('click');
expect(handleCompletionDateChange).toHaveBeenCalled();
})
The same to validate this.setState({ showInformation: false }); part. We need to figure out how to validate that based on render results.
Something like
expect(wrapper1.find("[data-id='information-block']")).toHaveLength(0);

Jest mocking with react calling actual event handler after calling mock function

I have this component test
const component = mount(<MemoryRouter><Login/></MemoryRouter>);
const emailField = component.find('input[type="email"]');
const passwordField = component.find('input[type="password"]');
const mockedSubmitFunction=jest.fn();
component.find(Login).instance().onSubmit=mockedSubmitFunction;
emailField.simulate('change', { target: { value: testEmail } });
passwordField.simulate('change', { target: { value: testPassword } });
component.find('form').simulate('submit');
expect(mockedSubmitFunction).toBeCalled();
and in the component i have
in constructor :-
this.onSubmit = this.onSubmit.bind(this);
and the eventhandler
onSubmit(event) {
event.preventDefault();
when i put a breakpoint in onSubmit it is coming to the component function after executing the mocked onSubmit, why is this happening.
I assumed it will only call the mocked onSubmit.
What am I doing differently?
CodeSandbox :https://codesandbox.io/s/q95lv7vlrw
But the sandbox is showing Could not find module in path: 'object-inspect/util.inspect' relative to '/node_modules/object-inspect/index.js' for some reason, which is unrelated i guess
So you got function mocked, but actual onSubmit is called. Instead if you want to call only mocked fn you have to provide it (as a prop in your test spec for example).
const mockedSubmitFunction = jest.fn(event => {
console.log("Mocked function");
});
const component = mount(
<MemoryRouter>
<Login login={mockedSubmitFunction} />
</MemoryRouter>
);
I updated sandbox for you.
You can additionally check this explained example on form testing.
Update: i suppose that the actual problem OP has was that mock function was firing, but it was copied to instance, thus expect...toBeCalled() fails (actual mockedFn was not called). You can avoid these problems by passing mocked function as a prop, spying on a function, etc.

Why is my button undefined in my test?

My test:
describe('button component', () => {
it('should toggle off when clicked', () => {
let component;
component = ReactTestUtils.renderIntoDocument(<Search />);
let searchbtn = ReactTestUtils.findRenderedDOMComponentWithTag(component, 'button');
ReactTestUtils.Simulate.click(searchbtn);
console.log(searchbtn, 'search button***'); //UNDEFINED
expect(searchbtn.calledOnce).to.equal(false);
})
});
This is my search component:
render() {
return (
<div className="search">
<button className="searchButton" onClick={this.handleSearch}>{this.state.on ? 'ON' : 'OFF'}</button>
</div>
);
}
Do I need to spy on it or mock it? or is there a better way to test buttons in react?
Since you have added enzyme tag I will answer using enzyme.
It can be tested very easily via shallow rendering -
const searchWrapper = shallow(<Search />);
const button = searchWrapper.find('button').first();
When you simulate click event on button the onClick handler provided via onClick prop which is handleSearch in your case will be called.
So if you are setting some state based on the onClick function call corresponding ui changes based on the state changes can be compared or checked if changes were reflecting correctly in the dom.
or
if you just want to check if method was called or not by using a fake method of similar name -
const onButtonClick = sinon.spy();
expect(onButtonClick.calledOnce).to.equal(false);
button.setProps({ onClick:onButtonClick});
button.simulate('click');
expect(onButtonClick.calledOnce).to.equal(true);

In a React Component, where should I bind arguments to my onClick functions?

So in an example component
<Button doStuff={doStuff} />
I know this is bad, it creates a new function on every render:
class Button extends Component {
const value = getClickValue(this.props); // do something with props
render() {
<button onClick={() => this.props.doStuff(value)}>Click me</button>
}
}
But not sure which is better for performance from the following:
1) Bind click value in a class property function
This is a trivial example, but in my current component the code to process the props is cluttering my component up and makes the component more smart and less dumb, when you want to strive to have DUMB components and smart containers.
class Button extends Component {
handleClick = () => {
const value = getClickValue(this.props); // do something with props to get value
this.props.doStuff(value);
}
render() {
<button onClick={this.handleClick}>Click me</button>
}
}
2) Send bound functions as props from a container to a stateless functional component.
This method seems better from an organizational perspective. The component is as dumb as possible and is only concerned with rendering. But does it impact performance? As in, is a new function constructed every time the ownProps changes?
const Button = (onClick) => (
<button onClick={this.props.onClick}>Click me!</button>
)
import { connect } from 'react-redux';
const mapDispatchToProps = (dispatch, ownProps) => {
const value = getClickValue(ownProps);
return {
onClick: () => doStuff(value)
};
};
connect(null, mapDispatchToprops)(Button);
In your second approach, every time your props change, mapDispatchToProps is called. Since you're returning an arrow function, it will create that function again.
That makes your first suggestion the best option.

Resources