How to mock component method using Jest and Enzyme - reactjs

I have a React component that has a text input as one of the children inside the main wrapper. When the input gains focus it calls a function via its onFocus property. So the component's structure is something like this:
<div className="main-wrapper">
<input type="text" onFocus={this.focusHandler} />
</div>
Elsewhere in the class there is a method named focusHandler that looks something like this
focusHandler = () => {
//Do whatever it is that focus handlers do.
//In this case that involves changing the local state, so there is something like
this.setState({ fieldHasFocus: true });
}
What I would like to do is to have a test (in Jest) that would verify that when the input gains focus the focusHandler() method is called. However, I can't figure out how to put a mock into my test for focusHandler() and to check whether or not it is called when I simulate('focus') on the input field.

You can spy on the before you render the component. You don't need to force update the instance of the component. Declare the spy function at the top of the file before your spec/describe block.
const focusHandlerSpy = jest.spyOn(YourComponent.prototype, 'focusHandler');
Then ...
describe('When the input field is focused', () => {
beforeEach(() => {
component.find('input').simulate('focus');
});
it('should invoke the focusHandlerSpy function', () => {
expect(focusHandlerSpy).toHaveBeenCalled();
});
});

Try something like this:
const wrapper = shallow(<YourComponent />);
const focusHandlerSpy = jest.spyOn(wrapper.instance(), 'focusHandler');
wrapper.instance().forceUpdate();
Now your focusHandlerSpy will be called on focus.

Related

Test React Form internal submit

I have this component:
function Form() {
const [value, setValue] = useState('');
const hanleSubmit = (ev) => {
ev.preventDefault();
console.log('submit', value);
}
return (
<form onSubmit={props.onSubmit}>
<input type="text" value={value} onChange={e => setValue(e.target.value)} />
<button type="submit">Add</button>
</form>
);
}
and the following test:
import React from "react";
import { render, fireEvent } from "react-testing-library";
import Form from "./Form";
it("submits", () => {
const onSubmit = jest.fn();
const { getByText } = render(<Form />);
fireEvent.click(getByText("Add"));
expect(onSubmit).toHaveBeenCalled(); // test fail with 0 called
});
I know I gotta to pass this mock function as prop.
But I'd like to know if exist a way to test internal function like handleSubmit without pass as prop?
I'm using react-testing-library.
You can use jest.spyOn(console, 'log') and check the handleSubmit is triggered indirectly. In fact, handleSubmit should have some more meaningful logic, not just console.log(). We should test the meaningful logic. But for the code you posted, then only we can do is above.
You should test the behavior of the component rather than an implementation detail.
The behavior of the component is: when you trigger the event handler, some states may change, the component will re-render, UI structure change, assert UI changes.
The implementation detail is: assert some function is called or not. This makes test cases vulnerable.
Test behavior or function, you don't need to pay attention to implementation details, regardless of the implementation details, we only test the final result.
If your component is just a presentation component with no state, just perform a Snapshot test to check the UI hierarchy.
If there are states, you need to test different states of the component.
If the state is promoted to the parent, then you test the parent and the state of the child on the UI when the parent state changes
Passing the mock onSubmit as a prop is a simple and common way.
If you want to handle some logic in Form component and call the onSubmit function passed from parent component in the same time.
You should write it like this:
const hanleSubmit = (ev) => {
ev.preventDefault();
// some logic for Form component itself, you should test these logic when you test Form component rather than `props.onSubmit()`.
console.log('submit', value);
// Parent component only care about the submitted value.
props?.onSubmit(value)
}

Jest spyOn handleSubmit() method does not exist

My test case goes like this:
describe('Personal Profile', () => {
it('renders', () => {
const wrapper = mount(
<PersonalProfile store={store}/>
);
const spy = jest.spyOn(wrapper.instance(), 'handleChangetype')
wrapper.update();
wrapper.find(Typeahead).at(2).simulate('change');
console.log(wrapper.find(Typeahead).at(2).simulate('change').debug())
expect(spy).toHaveBeenCalled();
});
});
I am getting the above error while running the test case.
In my js file, I have not used arrow functions and bound the method in the constructor though I m having this error.
can anybody help?
I am able to figure it out. the problem was that I used redux store and it was trying to find the method in the connect component. So what I needed to do is to call .first().shallow() on my wrapper to get the component I want.
const wrapper = shallow(
<PersonalProfile store={store}/>
).first().shallow().first().shallow();
Did this as I have two HOCs so get the desired component I have to shallow the first component of previous return twice.

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);

Testing onChange event with Jest

According to my code coverage, I need to test the function called in my onChange event. This is actually where I update my functional component's state using the useState hook.
Here is my component :
const Component:React.FC<{}> = () => {
const {value, setState} = useState('');
return(
<View>
<CustomComponent
onChange={(value) => setState(value)}
/>
</View>
)
}
The customComponent is a component derived from the React Input component.
When text is changed inside of it, it calls the onChange function passed as its prop with the text. This is how it comes up to the parent Component which sets the value of the text input in its state as displayed above.
My code coverage returns this analysis :
onChange={//(value) => setState(value)//}
Where the code between the // has to be covered. I don't really understand how I can cover this. First thought was use mock functions, but I can't seem to find how to mock it to the onChange event since I don't pass anything as prop to the main Component.
After a few tests, I finally understood that the coverage wasn't asking for the actual test of the onChange function but actually the value that is evaluated. Therefore, here is what I am doing:
Fetching the TextInput Child component
Changing its Text
Evaluating what it renders
I am using #testing-library/react-native here because it makes selecting tree components easier with the use of accessibilityLabel for example (It actually made me understand the importance of that prop).
Here is what a test looks like:
describe('Testing useState functions', () => {
test('test', () => {
//Rendering the component and its tree
const { container, getByLabelText } = render(<SignupView />);
//Extracting the child, username_input component with his accessibilityLabel
const username_input = getByLabelText('username_input');
const email_input = getByLabelText('email_input');
//Fire a native changeText event with a specific value
fireEvent.changeText(username_input, 'doe');
fireEvent.changeText(email_input, 'doe#joe.com');
//Checking the rendered value
expect(username_input.props.value).toEqual('doe');
expect(email_input.props.value).toEqual('doe#joe.com');
});
});

Resources