Jest spyOn handleSubmit() method does not exist - reactjs

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.

Related

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

useContext values return undefined in testing

I am new to testing and using Enzyme and Jest to write very simple tests. I just want to check whether the component renders or not. However, (I guess) because my component uses useContext hook, test case automatically returns undefined for all values come from the Context.
In component:
const { count, setCount } = useContext(Context);
Test case:
it('should render', () => {
const component = shallow(<MyComponent />);
const wrapper = component.find('myClassName');
expect(wrapper.length).toBe(1);
});
Test Result: Cannot read property 'count' of undefined. I don't know what I am doing wrong. Is there a simple way that always works with useContext and other hooks to test simple things?
I think the problem here is that when you shallow rendering a component, Context will be ignored. So try mounting your component instead of shallow rendering it like so:
import { mount } from "enzyme"; // mount instead of `shallow` here
...
it('should render', () => {
const component = mount(<MyComponent />); // `mount` here as well
const wrapper = component.find('myClassName');
expect(wrapper.length).toBe(1);
});

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

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.

Testing a React component with Enzyme. Typescript can't find instance methods

I want to test a React class component.
Let's say I have a method in my class that computes something based on the current state and props.
import Component from './Component'
const wrapper = enzyme.shallow(<Component {...props} />);
it('does something', () => {
expect(wrapper.instance().someInstanceMethod(input)).toBe(true);
});
Typescript says Property 'someInstanceMethod' is not defined on type Component<any, any>. How can I tell Typscript how my class is looking and what methods it has?
Is there a good example for this?
You can set the component type in the call to shallow. This is a little bit of boilerplate, but it makes things typesafe. The good thing is that the wrapper is typesafe, not just the instance you pull out.
import Component from './Component'
// Wrapper will know the type of the component.
const wrapper = enzyme.shallow<Component>(<Component {...props} />);
it('does something', () => {
expect(wrapper.instance().someInstanceMethod(input)).toBe(true);
// You can also get the state from the wrapper.
expect(wrapper.state().someComponentState).toBeTruthy();
});
One possible solution (thanks to the comment from marzelin) is to explicitly declare the type of the instance() method. There might be more elegant ways to do this.
import Component from './Component'
const wrapper = enzyme.shallow(<Component {...props} />);
const instance = wrapper.instance() as Component; // explicitly declare type
it('does something', () => {
expect(instance.someInstanceMethod(input)).toBe(true); // no TS error
});
Thanks to #marzelin and #Chris!
Other possible solution
import Component from './Component'
const wrapper = enzyme.shallow(<Component {...props} />);
const instance = wrapper.instance() as any; // explicitly declare type
it('does something', () => {
expect(instance.someInstanceMethod(input)).toBe(true); // no TS error
});
This comes in handy where someInstanceMethod receives event as parameter, explicitly declare type as component requires you to pass whole event object which is not something a developer want for writing test cases.

Resources