Jest Mock returns undefined instead of value - reactjs

I am using Jest to test a react component. I am trying to mock a function from other dependency. The function from dependency should return an array, but it is showing undefined on the console.
Below file is the tsx file, when I click the button, it should call the dependency function to get the list of the Frames.
ExitAppButton.tsx:
import React, { useContext, useState } from 'react';
import { TestContext } from '../ContextProvider';
import { useDispatch } from 'react-redux';
const ExitAppButton = (props: any): JSX.Element => {
const { sdkInstance } = useContext(TestContext);
const exitAppClicked = () => {
const appList = sdkInstance.getFrames().filter((app: any) => {app.appType === "Test App"}).length}
test file, SignOutOverlay.test.tsx:
import * as React from 'react';
import { fireEvent, render, screen } from '#testing-library/react';
import SignOutOverlay from '.';
import ExitAppButton from './ExitAppButton';
import { TestContext } from '../ContextProvider';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';
const api = require('#praestosf/container-sdk/src/api');
const mockStore = configureStore([]);
jest.mock('#praestosf/container-sdk/src/api');
api.getFrames.mockReturnValue([{appType:"Test App"},{appType:"Test App"},{appType:"Not Test App"}]);
describe('Test Exit app Button', () => {
const renderExitAppButton = () => {
const store = mockStore([{}]);
render(
<Provider store={store}>
<TestContext.Provider value={{ sdkInstance: api }}>
<SignOutOverlay>
<ExitAppButton/>
</SignOutOverlay>
</TestContext.Provider>
</Provider>
);
};
it('should to be clicked and logged out', () => {
renderExitAppButton();
fireEvent.click(screen.getByTestId('exit-app-button-id'));
});
This is the dependency file, api.js
const getFrames = () => {
let frames = window.sessionStorage.getItem('TestList');
frames = frames ? JSON.parse(frames) : [];
return frames
};
const API = function () { };
API.prototype = {
constructor: API,
getFrames
};
module.exports = new API();
I mocked the getFrame function to return an array of 3 objects, but when running the test case, it is returning undefined. Below error was showing:
TypeError: Cannot read property 'filter' of undefined
Am I mocking this correct?

I think it's because api.getFrames is undefined and not a mock.
Try changing your mock statement to this:
jest.mock('#praestosf/container-sdk/src/api', () => ({
getFrames: jest.fn(),
// add more functions if needed
}));

Turns out, I have the other file with the same test name which is causing the problem. I am beginner for Jest, a tip for developer like me, we should always run test case file alone using
jest file.test.tsx
Not all files at a time:
jest

Related

Mock method from external file called by a React component

I have a React component which is calling methods defined in another file, something like this:
import { realMethod } from '../some-library';
export const MyComponent = () => {
const result = realMethod();
const field = result.someField;
/* Rest of the code... */
}
I want to write a test for MyComponent using react-testing-library and it's breaking because when it comes to the line const field = result.someField it doesn't know what it is (TypeError: Cannot read properties of undefined (reading 'someField').
So far I wrote this:
import * as React from 'react';
import { render, screen, userEvent, within } from '../../test-utils';
import { MyComponent } from './MyComponent';
import * as realMethods from '../some-library';
describe('<MyComponent />', () => {
it('should do stuff', () => {
const spyMethod = jest.spyOn(
realMethods,
'realMethod',
);
render(
<MyComponent />,
);
/* ... */
});
});
How can I mock realMethod and make it return something useful?
The spyOn itself shouldn't break your code. It looks like your realMethod does not return an object that contain someField, e.g.:
export const realMethod = () => ({ someField: 42 })
You can mock it like this:
jest.spyOn(realMethods, 'realMethod').mockReturnValue({ someField: 'mocked' });

Testing with jest on component with custom hook

i'm trying to write test code for my component that uses a custom hook to seperate logic from view
The problem is that i cannot for whatever reason seem to actually mock this custom hook in a test.
Following is a code example of what im trying to do:
// click-a-button.tsx
import {useClickAButton} from "./hooks/index";
export const ClickAButton = () => {
const { handleClick, total } = useClickAButton();
return <button onClick={handleClick}>{total}</button>;
}
// hooks/use-click-a-button.tsx
import React, {useCallback, useState} from 'react';
export const useClickAButton = () => {
const [total, setTotal] = useState<number>(0);
const handleClick = useCallback(() => {
setTotal(total => total + 1);
}, []);
return {
handleClick,
total,
};
}
// click-a-button.test.tsx
import * as React from 'react';
import {act} from "react-dom/test-utils";
import {render} from "#testing-library/react";
import {useClickAButton} from './hooks/index'
import {ClickAButton} from "./index";
const hooks = { useClickAButton }
test('it runs with a mocked customHook',() => {
const STATE_SPY = jest.spyOn(hooks, 'useClickAButton');
const CLICK_HANDLER = jest.fn();
STATE_SPY.mockReturnValue({
handleClick: CLICK_HANDLER,
total: 5,
});
const component = render(<ClickAButton />);
expect(component.container).toHaveTextContent('5');
act(() => {
component.container.click();
});
expect(CLICK_HANDLER).toHaveBeenCalled();
})
When running the test, neither of the expects is fulfilled.
Context gets to be 0 instead of the mocked 5 and the CLICK_HANDLER is never called.
All in all it seems that the jest.spyon has no effect.
Please help
it seems i found the answer myself.
// right after imports in test file
jest.mock('./hooks')
is all that it took!

Testing custom hook: Invariant Violation: could not find react-redux context value; please ensure the component is wrapped in a <Provider>

I got a custom hook which I want to test. It receives a redux store dispatch function and returns a function. In order to get the result I'm trying to do:
const { result } = renderHook(() => { useSaveAuthenticationDataToStorages(useDispatch())});
However, I get an error:
Invariant Violation: could not find react-redux context value; please ensure the component is wrapped in a
It happens because of the useDispatch and that there is no store connected. However, I don't have any component here to wrap with a provider.. I just need to test the hook which is simply saving data to a store.
How can I fix it?
The react hooks testing library docs go more into depth on this. However, what we essentially are missing is the provider which we can obtain by creating a wrapper. First we declare a component which will be our provider:
import { Provider } from 'react-redux'
const ReduxProvider = ({ children, reduxStore }) => (
<Provider store={reduxStore}>{children}</Provider>
)
then in our test we call
test("...", () => {
const store = configureStore();
const wrapper = ({ children }) => (
<ReduxProvider reduxStore={store}>{children}</ReduxProvider>
);
const { result } = renderHook(() => {
useSaveAuthenticationDataToStorages(useDispatch());
}, { wrapper });
// ... Rest of the logic
});
This is probably a late answer but you can also use this in your test
jest.mock('react-redux', () => {
const ActualReactRedux = jest.requireActual('react-redux');
return {
...ActualReactRedux,
useSelector: jest.fn().mockImplementation(() => {
return mockState;
}),
};
});
This issues is related your test file. You have to declarer provider and store in your test file.
Update or replace your app.test.tsx by below code
NB: Don't forget to install redux-mock-store if you don't have already.
import React from 'react';
import { render } from '#testing-library/react';
import App from './App';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';
describe('With React Testing Library', () => {
const initialState = { output: 10 };
const mockStore = configureStore();
let store;
it('Shows "Hello world!"', () => {
store = mockStore(initialState);
const { getByText } = render(
<Provider store={store}>
<App />
</Provider>
);
expect(getByText('Hello World!')).not.toBeNull();
});
});
I got this solution after searching 1 hours.
Thanks a lot to OSTE
Original Solution: Github issues/8145 and solution link
With this solution if you get error like TypeError: window.matchMedia is not a function then solve by this way. add those line to your setupTests.ts file. Original solution link stackoverflow.com/a/64872224/5404861
global.matchMedia = global.matchMedia || function () {
return {
addListener: jest.fn(),
removeListener: jest.fn(),
};
};
I think you can create test-utils.[j|t]s(?x), or whatever you set the name of the file to, like this:
https://github.com/hafidzamr/nextjs-ts-redux-toolkit-quickstart/blob/main/__tests__/test-utils.tsx
//root(or wherever your the file)/test-utils.tsx
import React from 'react';
import { render, RenderOptions } from '#testing-library/react';
import { Provider } from 'react-redux';
// Import your store
import { store } from '#/store';
const Wrapper: React.FC = ({ children }) => <Provider store={store}>{children}</Provider>;
const customRender = (ui: React.ReactElement, options?: Omit<RenderOptions, 'wrapper'>) => render(ui, { wrapper: Wrapper, ...options });
// re-export everything
export * from '#testing-library/react';
// override render method
export { customRender as render };
Use it like this:
https://github.com/hafidzamr/nextjs-ts-redux-toolkit-quickstart/blob/main/__tests__/pages/index.test.tsx
//__tests__/pages/index.test.tsx
import React from 'react';
import { render, screen } from '../test-utils';
import Home from '#/pages/index';
describe('Home Pages', () => {
test('Should be render', () => {
render(<Home />);
const getAText = screen.getByTestId('welcome');
expect(getAText).toBeInTheDocument();
});
});
Works for me.
screenshot work
BTW, if you place the test-utils.[j|t]s(?x) or whatever you set the name file place on the directory __test__, don't forget to ignore it on jest.config.js.
//jest.config.js
testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/.next/', '<rootDir>/__tests__/test-utils.tsx'],
repo: https://github.com/hafidzamr/nextjs-ts-redux-toolkit-quickstart

Context API Unit test failing with TypeError

I have a HOC component WithContext (in a file conveniently named withContext.js) as follows
import React from 'react';
import { DEFAULT_STATE } from './store';
const MyContext = React.createContext(DEFAULT_STATE);
export function WithContext(Component) {
return function WrapperComponent(props) {
return (
<MyContext.Consumer>
{state => <Component {...props} context={state} />}
</MyContext.Consumer>
);
};
};
and a component that uses it as follows
import React from "react";
import { WithContext } from './withContext';
const MyComp = (context) => {
return (
<div className="flex dir-col" id="MyComp">
<p>This is a test</p>
</div>
)
};
export default WithContext(MyComp);
I also have a unit test that aims to test this MyComp component. The unit test follows
import React from "react";
import {shallow} from "enzyme";
import Enzyme from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import { WithContext } from './withContext';
// We need to configure our DOM
import jsdom from 'jsdom'
const {JSDOM} = jsdom;
const {document} = (new JSDOM('<!doctype html><html><body></body></html>')).window;
global.document = document;
global.window = document.defaultView
Enzyme.configure({
adapter : new Adapter()
})
beforeEach(() => {
jest.resetModules()
})
//Takes the context data we want to test, or uses defaults
const getMyContext = (context = {
state : {}
}) => {
// Will then mock the MyContext module being used in our MyComp component
jest.doMock('withContext', () => {
return {
MyContext: {
Consumer: (props) => props.children(context)
}
}
});
// We will need to re-require after calling jest.doMock.
// return the updated MyComp module that now includes the mocked context
return require('./MyComp').default;
};
describe("MyComp component loading check", () => {
test("Renders the MyComp component correctly", () => {
const MyCompContext = getMyContext();
const wrapper = shallow(<MyComp/>);
// make sure that we are able to find the header component
expect(wrapper.find(".flex").hostNodes().length).toBe(1);
});
});
However this test keeps failing with the message
TypeError: (0 , _withContext.WithContext) is not a function
};
export default WithContext(MyComp);
Can you please let me know what is wrong here ?
Thanks
Looks like you are mocking withContext with jest.doMock but the mock you are returning from the factory function does not contain a WithContext function.
Then when you require('./MyComp').default it is using the withContext mock within your MyComp module and failing when it tries to export default WithContext(MyComp); since the withContext mock does not define a WithContext function.

Props aren't passing inside component in test cases written with Jest and Enzyme

This is my test case
import React from 'react';
import { mount } from 'enzyme';
import CustomForm from '../index';
describe('Custom Form mount testing', () => {
let props;
let mountedCustomForm;
beforeEach(() => {
props = {
nav_module_id: 'null',
};
mountedCustomForm = undefined;
});
const customform = () => {
if (!mountedCustomForm) {
mountedCustomForm = mount(
<CustomForm {...props} />
);
}
return mountedCustomForm;
};
it('always renders a div', () => {
const divs = customform().find('div');
console.log(divs);
});
});
Whenever I run the test case using yarn test. It gives the following error TypeError: Cannot read property 'nav_module_id' of undefined.
I have already placed console at multiple places in order to see the value of props. It is getting set. But it couldn't just pass through the components and give the error mentioned above.
Any help would be appreciated been stuck for almost 2-3 days now.
You have to wrap the component that you want to test in beforeEach method such that it becomes available for all the 'it' blocks, and also you have to take the mocked props that you think you are getting into the original component.
import React from 'react'
import {expect} from 'chai'
import {shallow} from 'enzyme'
import sinon from 'sinon'
import {Map} from 'immutable'
import {ClusterToggle} from '../../../src/MapView/components/ClusterToggle'
describe('component tests for ClusterToggle', () => {
let dummydata
let wrapper
let props
beforeEach(() => {
dummydata = {
showClusters: true,
toggleClustering: () => {}
}
wrapper = shallow(<ClusterToggle {...dummydata} />)
props = wrapper.props()
})
describe(`ClusterToggle component tests`, () => {
it(`1. makes sure that component exists`, () => {
expect(wrapper).to.exist
})
it('2. makes sure that cluster toggle comp has input and label', () => {
expect(wrapper.find('input').length).to.eql(1)
expect(wrapper.find('label').length).to.eql(1)
})
it('3. simulating onChange for the input element', () => {
const spy = sinon.spy()
const hct = sinon.spy()
hct(wrapper.props(), 'toggleClustering')
spy(wrapper.instance(), 'handleClusterToggle')
wrapper.find('input').simulate('change')
expect(spy.calledOnce).to.eql(true)
expect(hct.calledOnce).to.eql(true)
})
})
})

Resources