Jest - test method that passes to child component - reactjs

I'm new in testing React component, so I'm trying test some method in parent component that passes to child component as props. I use Jest and Enzyme for this.
My test:
it('should be able to call myMethod callback', () => {
const mockFn = jest.fn();
ParentComponent.prototype.myMethod = mockFn;
const wrapper = Enzyme.shallow(<ParentComponent />);
wrapper.find('ChildComponent').props().myMethod();
expect(mockFn).toHaveBeenCalledTimes(1);
});
The test passed but myMethod didn't invoke (myMethod doesn't covered by tests).
When I use:
wrapper.instance().myMethod = mockFn;
instead of:
ParentComponent.prototype.myMethod = mockFn;
everything is the opposite - myMethod is invoked, but the test is failed with error:
expect(jest.fn()).toHaveBeenCalledTimes(1)
Expected mock function to have been called one time, but it was called zero times.
How can I solve this? Thanks in advance.

You are assigning myMethod to a mock function which doesn't do anything, not even your original myMethod. When you are creating jest.fn(), what it basically does is creating a dummy mock function which doesn't do anything. So when you call wrapper.find('ChildComponent').props().myMethod(), it just call that function but not your myMethod.
What you might want to do is to spy on your method instead.
jest.spyOn(ParentComponent.prototype, 'myMethod'); // ParentComponent.prototype.myMethod is now a mock function
expect(ParentComponent.prototype.myMethod).toHaveBeenCalledTimes(1); // it works

Related

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.

Test that React prop method has not been called with Jest

How do I test a prop(callback) function that is called conditionally
I have a component which passes a prop function to its child on a condition like:
if(this.props.myFun) {
this.props.myFun();
}
to test this, I have two cases here:
1. Where the prop is passed to the component
<ChildComp myFun={value => value } /> and I can test it from the child-
const comp = mountWithIntl(<ChildComp myFun={value => value } />);
expect(comp.instance().props.myFun).toHaveBeenCalled();
Where the prop is not passed: I trying like
const comp = mountWithIntl(<MyComp />);
expect(comp.instance().props.myFun).not.toHaveBeenCalled();
But, Since If I mock the prop while mounting the component, the
method will be called. But how do I test the undefined or unmocked
prop?
If I dont pass the prop and do:
expect(comp.instance().props.myFun).not.toHaveBeenCalled();
I will get:
jest.fn() value must be a mock function or spy
which I cant do as per my code
please help
I don't think you can test that a not passed function does not get called. What you can do to test if it is get rendered without that function. That should be enough

Testing functions inside stateless React component with Enzyme

I have a stateless component:
export default function TripReportFooter(props) {
const { tripReport, user, toggleFavorite, navigation } = props;
handleShare = async slug => {
try {
const result = await Share.share({
message: `Check out this Trip Report:\n/p/${slug}/`
});
if (result.action === Share.sharedAction) {
if (result.activityType) {
} else {
// shared
}
} else if (result.action === Share.dismissedAction) {
}
} catch (error) {
alert(error.message);
}
};
handleFavorite = async id => {
const token = await AsyncStorage.getItem("token");
toggleFavorite(id, token);
};
return (
... // handleFavorite and handleShare called with TouchableOpacities.
);
}
It has two functions inside, handleShare and handleFavorite. I want to test these functions are called, and also that handleFavorite calls the prop function toggle favorite.
I tried wrapper.instance().handleFavorite(), but since it is a stateless component, it returns null.
Next someone on Stack Overflow suggested using a spy like so:
wrapper = shallow(<TripReportFooter {...props} handleFavorite={press} />);
wrapper
.find("TouchableOpacity")
.at(0)
.simulate("press");
expect(press.called).to.equal(true);
but this returned
'TypeError: Cannot read property 'equal' of undefined'.
What's the proper way to call these functions?
You first need to think about what you want to test. Is it implementation details or the interaction with your component? The latter is a much better mindset and standpoint so what I would do is to test the component from the interaction point of view.
I would (for handleShare):
Mock the Share object methods that are being called inside the share function;
Select the button I want to click/touch
Click/touch the button
Assert that the methods were called.
Now for the handleFavorite:
Mock AsyncStorage.getItem;
Create a fake toggleFavorite function that I would pass as props;
Select the button I want to click/touch
Click/touch the button
Assert my toggleFavorite function has been called
If you want to test these functions individually you would have to extract them to the outside of the component and test them individually. But I would not advise this as it is not clean and extra work.
Hope it helps!
Functions within a functional component aren't defined on the prototype or the functional component instance, you cannot directly spy on them
The solution here is to test out the internal implementation of the individual functions
For instance for handleFavourite function you can mock AsynStorage and pass on a mock function for toggleFavourite and then asset it its called on TouchableOpacity onPress simulation
You can check how to mock AsyncStore in this post:
How to test Async Storage with Jest?
const mocktToggleFavourite = jest.fn();
wrapper = shallow(<TripReportFooter {...props} toggleFavourite={mocktToggleFavourite} />);
wrapper
.find("TouchableOpacity")
.at(0)
.simulate("press");
expect(mockToggleFavourite).toHaveBeenCalled();
Similarly you can test the individual functionalities within handleShare by first mocking Share.share and then checking against each condition.For instance you can add an spy on window.alert and see if that is called
const windowSpy = jest.spyOn(window, 'alert');
wrapper = shallow(<TripReportFooter {...props} toggleFavourite={mocktToggleFavourite} />);
//Simulate event that calls handleShare
// Mock Share to give required result
expect(windowSpy).toBeCalledWith(expectedValue);

Test React component method is calling function pass as a prop

I want to test that when calling a method from a React component it trigger a function pass to the component as a props.
The method is something like this:
customMethod() {
// Do something
this.props.trackEvent({
category: 'eventCategory',
action: 'eventAction',
label: 'eventAction',
});
// Do something else
}
The method can be called from different ways, so I want just to make a generic test: if customMethod is called, should trigger this.props.trackEvent with data.
Is there a way to trigger a method call using jest and/or enzyme? I've read about doing something like this:
const wrapper = shallow(<AdPage {...baseProps} />);
wrapper.instance().customMethod();
But it is not working… any ideas.
I'm pretty new in the testing, so maybe should I use a different approach to this kind of tests?
Assuming your customMethod is a component method, I would test it like this:
(1) Fake your trackEvent prop as a jest.fn() when you create the wrapper.
(2) Call your customMethod using wrapper.instance().customMethod();
(3) Ensure props.trackEvent to haveBeenCalledWith the argument you mentioned.
As an example:
test('customMethod should call trackEvent with the correct argument', () => {
const baseProps = {
// whatever fake props you want passed to the component
// ...
trackEvent: jest.fn(),
};
const wrapper = shallow(<AdPage {...baseProps} />);
wrapper.instance().customMethod();
expect(baseProps.trackEvent).toHaveBeenCalledTimes(1);
expect(baseProps.trackEvent).toHaveBeenCalledWith({
category: 'eventCategory',
action: 'eventAction',
label: 'eventAction',
});
});

How can I unit test a component which dispatches an action in reactjs/redux app?

I have a reactjs component that has a method doSomething:
doSomething()
{
this.setState({
checked: !this.state.checked
}, function () {
if (this.state.checked) {
store.dispatch({type: 'SOMEACTION', data: true});
}
});
}
What would the chai/mocha unittest look like for this method/component? How can I test a dispatch has called?
Replace the dispatch with a spy and check that is was called with the correct arguments.
You can test that your action correctly dispatched to reducers correctly in another unit test somewhere using redux-mock-store.
You can also use redux-mock-store to mock the store here.
Although why are you calling dispatch on the store directly? You usually want to just call it on the props like so: this.props.dispatch() as dispatch gets passed automatically when you use mapStateToProps. This would mean you could just pass a spy down as a prop instead and not have to mock out the entire store.
You could use expect, for instance use spy to ensure the method is called as well as assert that state has changed.
// spy on the method to assert that it has been called
const doSomething = expect.spyOn(ExampleComponent, 'doSomething');
// simulate some behaviour
...
// assert method called
expect(doSomething).totHaveBeenCalled();
// assert state changed
assert.equal(store.getState()..., ...)
Also, you can split this into multiple tests.

Resources