Mocked function get triggered but toHaveBeenCalledWith do not recognize it - javascript

I mocked the useNavigation() hook from react-naitive/navigation but jest do not recognize it.
Jest gives me an error that the function did not get called, but when I log the function it got to be triggered.
I already tried it with wrapping they expect with waitFor but then I just get a typescript catch error
Error: Uncaught [TypeError: Cannot read properties of undefined (reading 'catch')]
My jest test:
jest.mock('#react-navigation/native', () => {
const actualNav = jest.requireActual('#react-navigation/native');
return {
...actualNav,
useNavigation: () => ({
// when I do () => { console.log('trigger') } it will be called
navigate: jest.fn(),
}),
useRoute: () => ({
params: { email: '' },
}),
};
});
....
it.only('should navigate to resend screen when button is pressed', async () => {
const { getByTestId } = render(
withStoreProvider(<EmailVerificationPending />),
);
await waitFor(() => {
expect(
getByTestId('emailVerificationPendingScreen_moreInfoCta'),
).toBeTruthy();
});
fireEvent.press(getByTestId('emailVerificationPendingScreen_moreInfoCta'));
expect(navigation.navigate).toHaveBeenCalledWith(
RootScreens.SignUpNavigator,
{
screen: SignUpScreens.EmailVerificationResend,
params: {
email: '',
},
},
);
});
Error message:
● features/signup/presentation/email-verification/pending-screen › should navigate to resend screen when button is pressed
expect(jest.fn()).toHaveBeenCalledWith(...expected)
Expected: "RootScreens_SignUpNavigator", {"params": {"email": ""}, "screen": "SignUpScreens_EmailVerificationResend"}
Number of calls: 0
64 | fireEvent.press(getByTestId('emailVerificationPendingScreen_moreInfoCta'));
65 |
> 66 | expect(navigation.navigate).toHaveBeenCalledWith(

This error message is likely occurring when you are trying to test a mocked function using the Jest testing framework, and the test is failing because the mocked function is being called but the assertion toHaveBeenCalledWith is not recognizing it.
Here are some possible solutions:
Make sure that you are importing the correct mock function and that
it is being called in the correct place in your code.
Check that the arguments passed to the mocked function match the
arguments passed to toHaveBeenCalledWith
check if the mock implementation is correct, you should use
jest.fn() or jest.spyOn() to create a mock function
Make sure that you are using the correct syntax for the
toHaveBeenCalledWith assert.
Try to debug the test to check if the mocked function is getting
called or not.
Make sure that you are correctly importing and calling the
navigation function, and that it is being called with the correct
arguments and parameters.
Make sure that the navigation function is being called after the
button press event and not before.
Make sure that the navigation function is being called in the
correct screen and the test is not looking for it in wrong screen.
Try using .toHaveBeenCalled() instead of .toHaveBeenCalledWith()
Make sure that the test case is not getting skipped or ignored.

Related

In Jest, how to test a function declared inside a function?

I am trying to test a function that is declared inside another function(Parent). I tried a few things but was not able to test that. I am using react library and JEST testing framework in the project.
Here is the service file (service.ts) function which I am trying to test:
import { useThisHook } from './base-hooks';
export function someFunction(param: string) {
const [
resolveFunction,
,
{ loading, error },
] = useThisHook();
const childFun = (param : number) => {
resolveFunction(someArguments);
}
return{
someValue: childFun
}
}
Here is the spec file code:
import * as SomeHooks from './base-hooks';
import * as Service from './service';
describe('Service function', () => {
beforeEach(() => {
jest.spyOn(SomeHooks, 'useThisHook').mockReturnValue(
{
//Some Value
}
);
});
test('Some function test', () => {
const someFunctionResponse = Service.someFunction('string');
expect(someFunctionResponse).toEqual({
someValue: expect.any(Function),
});
});
});
Till here it is fine. Tests are also getting passed but the problem is that childFn is not getting the coverage and that's what my requirement is. I am new to React and Jest. I am not able to understand how can I achieve that. I tried many things but didn't succeed.
Your childFn is not being called, that's why it's not getting coverage.
You could either refactor the whole childFn to a new hook and test it individually with something like react-hooks-testing-library.
Or you could separate the childFn and declarate it outside the someFunction scope pass the resolveFunction and then test it.
You only get coverage if the code is actually called, not when it is declared.

In Enzyme, how to get a function from the props of a functional component?

I am writing unit tests for my react project using Jest and Enzyme.
As shown below, I passed a function named updateUser to the to-be-tested component EditCard via props.
describe('The EditCard screen', () => {
let wrapper;
beforeEach(() => {
const defaultProps: Partial<EditCardProps> = {
toggleEditing: jest.fn(),
user: mockUsers[0],
updateUser: jest.fn(), // passes this function to the "EditCard" component via props
showSnackbar: jest.fn(),
};
wrapper = shallow(<EditCard {...(defaultProps as EditCardProps)} />);
});
Then I want to test how many times it was called after simulating a click on a button.
it('should match the snapshot when the "Name" textfield is not filled and the "submit" button is clicked', () => {
wrapper.find('#Name').simulate('change', { target: { value: null } });
wrapper.find('#submit').simulate('click');
// Try to get the "updateUser" function from the props, but get "undefined".
expect(wrapper.prop('updateUser')).toHaveBeenCalledTimes(0);
});
But I got the error shown below:
Matcher error: received value must be a mock or spy function
Received has value: undefined
24 | wrapper.find('#Name').simulate('change', { target: { value: null } });
25 | wrapper.find('#submit').simulate('click');
> 26 | expect(wrapper.prop('updateUser')).toHaveBeenCalledTimes(0);
Could someone tell me where I did wrong? Why I cannot get the function from the props and undefined was returned?
Thanks in advance!
A few tweaks to your code should have it working...
import * as React from "react";
import { mount, ReactWrapper } from "enzyme";
import EditCard from "../path/to/EditCard";
/*
I'd recommend defining jest fns here to make them easier to reference anywhere
within the tests below; otherwise, it'll have to referenced via
'defaultProps.updateUser', 'defaultProps.showSnackbar', ...etc.
Using 'const' here allows us to define these variables within the
module's closure -- in short, only accessible within these tests and NOT
globally accessible (from other file tests).
*/
const showSnackbar = jest.fn();
const toggleEditing = jest.fn();
const updateUser = jest.fn();
/*
if the EditCard component is properly typed, then you shouldn't need to
add types to this 'defaultProps' object
*/
const defaultProps = {
showSnackbar,
toggleEditing,
updateUser,
user: mockUsers[0]
};
describe('The EditCard screen', () => {
let wrapper: ReactWrapper;
beforeEach(() => {
/*
I'd recommend mount over shallow because child components can be
deeply nested and require multiple .dive calls; however, if
you know the child components of "EditCard" are just simple JSX elements,
then shallow will be fine
*/
wrapper = mount(<EditCard {...defaultProps} />);
});
it("should not call 'updateUser' when the form is submitted with an empty '#Name' field", () => {
/*
I'm not sure what simulating "null" does for this input, but assuming this
input is required you should at least pass a string value -- assuming
"#Name" input is of type 'text' | 'password' | 'email' => string and
not a number. On a related note, if it's required, then simply remove this
code as it doesn't do much for the test.
*/
// wrapper.find('#Name').simulate('change', { target: { value: "" } });
/*
I'm assuming this then simulates a form submit. Unfortunately,
pressing the submit button won't work. Instead you'll have to
simulate a form submit. This is a limitation of Enzyme and last I
checked hasn't been/can't be fixed.
*/
wrapper.find('form').simulate('submit');
/*
it should then NOT call the jest.fn() "updateUser" when submitted since
'#Name' is empty. Notice that we're referencing 'updateUser' -- the jest fn
defined above -- and not the wrapper.prop fn.
*/
expect(updateUser).not.toHaveBeenCalled();
});
// include other tests...
});
Here's a working example (click the Tests tab to run tests):

How to test if localStorage receives item I set in a function?

Currently trying to run a unit test for a authentication function. I've written a dummy function that basically does what the authentication function for the sake of abiding by NDA.
Authentication.js
export const myTestFunction = param => {
console.log(param);
localStorage.setItem("Test", param)
}
index.test.js
import {myTestFunction} from "../../redux/actions/ApiAuthAction";
const localStorageMock = {
getItem: jest.fn(),
setItem: jest.fn(),
clear: jest.fn()
};
global.localStorage = localStorageMock;
test("Test to see myTestFunction sets localStorage to the param value", () => {
let param = "Hello"
myTestFunction(param);
expect(localStorageMock.setItem).toBeCalledWith("Test");
});
When I run the test, this is what it returns:
expect(jest.fn()).toBeCalledWith(expected)
Expected mock function to have been called with:
["Test"]
But it was not called.
73 | let param = "Hello"
74 | myTestFunction(param);
> 75 | expect(localStorageMock.setItem).toBeCalledWith("Test");
| ^
at Object.toBeCalledWith (src/__tests__/reducers/index.test.js:75:38)
I am not sure what I am doing wrong here. I am creating a mock localStorage. I threw the function into the test and it still throws this failed test of it not being called. Help?
EDIT: The whole point of this test is to see if that localStorage.setItem("Test, param); gets called or not.
The solution I ended up using was: expect(localStorage.getItem("Test")).toBeDefined(); but I am happy to hear if anyone has a more dynamic solution.

Why isn't the following expect ... toThrowError logging anything in my terminal?

I have a helper that just throws an error:
export const checkPanoramaFormat = (panorama) => {
if (!panorama.objectId) {
throw new Error('panorama id is required')
}
}
This is my test:
import {
checkPanoramaFormat
} from '#/common/helpers'
describe('checkPanoramaFormat', () => {
const panorama = { anotherProp: '1' }
it('creates clone', () => {
expect(checkPanoramaFormat(panorama)).toThrowError('hshshs')
})
})
I expected the terminal to show me what was expected and what I got. But I got nothing. In fact, Jest isn't telling me anything:
Why is this and how to fix it?
Try instead:
expect(() => {
checkPanoramaFormat(panorama)
}).toThrow('hshshs')
If you call the function immediately as the expect argument, it will throw the exception before the assertion. It is not inside a try/catch block. You should instead pass a function handler to expect to be called only on assertion. I enclosed it in an arrow function, but it can be called other forms such as:
expect(myFunc) // no arguments
expect(myFunc.bind(this, arg1, arg2)) // using .bind to set arguments for later calling

Sinon spy callCount property returns 0 on some tests

This is my code:
// SUT.spec.js
import * as myModule from './myModule';
describe('my issue', () => {
let myFuncSpy = sinon.spy(myModule, 'myFunc');
beforeEach(() => {
myFuncSpy.reset();
});
it('my case A', () => {
SUT.methodA();
expect(myFuncSpy.callCount).to.equal(1); // fails, it says it's 0
});
it('my case B', () => {
SUT.methodB();
expect(myFuncSpy.callCount).to.equal(1); // passes
});
});
In my module, both method calls myFunc, however only on methodA it's not being registered:
// SUT.js
import { myFunc } from './myModule';
export function methodA() {
myFunc(....);
console.log(myFunc.callCount); // Mocha output shows 1
};
export function methodB() {
myFunc(....);
console.log('method B ran'); // Mocha output shows this line
console.log(myFunc.callCount); // Mocha output shows 1
};
Basically there is no apparent difference in the way the spy is being called. I'm very confused as what could be possible wrong.
I added the console.log statement inside the SUT just to make sure the spy was properly set (otherwise it wouldn't have a property called callCount). Also, if I comment out the .reset() call, the log statement shows undefined instead of 1 or another number.
What could be wrong in here? This is of course a simplified version of the actual SUT. However, the console.log statements show that the problem is definitely not that the lines are not being executed.
You have to await your asynchronous methods before asserting.

Categories

Resources