Jest Mocking useSelector is shown as untested in coverage report - reactjs

I was testing useSelector using the following code below. Although when I looked at my coverage report I saw in red that it wasn't actually being tested... Is there a way to test those lines?
import React from 'react';
import { render } from '../../../test-utlities/test-utlities';
import IndividualInvestor from './IndividualInvestor';
import * as redux from 'react-redux';
describe('Individual Investor', () => {
const useSelectorMock = jest.spyOn(redux, 'useSelector');
const useDispatchMock = jest.spyOn(redux, 'useDispatch');
beforeEach(() => {
useSelectorMock.mockClear();
useDispatchMock.mockClear();
});
test('Makes sure it reaches questionaire', async () => {
useSelectorMock.mockReturnValue({
offering: {
payment_enabled: false,
_id: '61aa480e4e18c1bbfba8f83d',
},
payment_enabled: false,
_id: '61aa480e4e18c1bbfba8f83d',
});
const dummyDispatch = jest.fn();
useDispatchMock.mockReturnValue(dummyDispatch);

Yes, actually run the code :) By mocking useSelector, you've skipped running all of its actual logic that takes your selector function and runs it when needed.
We would strongly recommend against mocking useSelector and useDispatch. Instead, create an actual Redux store in the test, and wrap the component under test with a <Provider>.
You can see examples of how to do this in a reusable render() test util in our docs guide on testing:
https://redux.js.org/usage/writing-tests#components

In addition to what markerikson said i managed to actually test the code with this
wrapper for the component with the provider
// test-utils.js
import React from 'react';
import { render as rtlRender } from '#testing-library/react';
import { configureStore } from '#reduxjs/toolkit';
import { Provider } from 'react-redux';
// Import your own reducer
import rootReducer from '../redux/reducer/combineReducers';
import { BrowserRouter as Router } from 'react-router-dom';
function render(
ui,
{
preloadedState,
store = configureStore({ reducer: rootReducer, preloadedState }),
...renderOptions
} = {}
) {
function Wrapper({ children }) {
return (
<Provider store={store}>
<Router>{children}</Router>
</Provider>
);
}
return rtlRender(ui, { wrapper: Wrapper, ...renderOptions });
}
// re-export everything
export * from '#testing-library/react';
// override render method
export { render };
Then i used that function render
const store = createStore(
rootReducer,
{
formBody: {
offering: {
data: {
payment_enabled: false,
_id: '61aa480e4e18c1bbfba8f83d',
},
},
},
},
applyMiddleware(thunk)
);
const { container } = render( <SomeComponent/>,
//Pass in store here!
{
store: store,
}
);

Related

Failing to mock child react component in tests

There are a bunch of articles and examples about this one, but for some reason, nothing seems to work.
I have a react component that has a child component. And, for simplicity's sake, I want to mock the child component in the test.
The component looks like this:
...
import { ProjectTeam } from '../../assignment/project-team/component';
export const ProjectOverview: React.FC<ProjectOverviewProps> = ({ projectId }) => {
...
return (
<>
...
<Box flex={1}>
<ProjectTeam projectId={projectId} />
</Box>
...
</>
);
};
The ProjectTeam component:
export const ProjectTeam: React.FC<ProjectTeamProps> = ({ projectId }) => {
// just a standard component...
};
And here is the test:
import React from 'react';
import configureMockStore from 'redux-mock-store';
import { Provider } from 'react-redux';
import { render } from '#testing-library/react';
import { I18nextProvider } from 'react-i18next';
import { generateProject } from '../../testing/generators/project';
import { ProjectOverview } from './component';
import { NGStateProvider } from '../../react/providers/route-provider/component';
import { testAngularRouter } from '../../testing/testAngularRouter';
import { DefaultProjectCollection, DefaultUtilizationCollection } from '../store/model';
import { DefaultApaActionCollection } from '../../apa/model';
import i18n from '../../testing/i18n';
import thunk from 'redux-thunk';
describe('ProjectOverview', () => {
let mockStore = null;
const project = generateProject();
beforeEach(() => {
jest.mock('../../assignment/project-team/component', () => ({ ProjectTeam: 'ProjectTeam' }));
mockStore = configureMockStore([thunk])({
projects: { DefaultProjectCollection, ...{ entities: { [project.id]: project } } },
toolUtilizations: DefaultUtilizationCollection,
apaActions: DefaultApaActionCollection,
});
});
test('renders correctly', () => {
const { asFragment } = render(
<NGStateProvider router={testAngularRouter}>
<Provider store={mockStore}>
<I18nextProvider i18n={i18n}>
<ProjectOverview projectId={project.id} />
</I18nextProvider>
</Provider>
</NGStateProvider>,
);
expect(asFragment()).toMatchSnapshot();
});
});
My assumption, that jest.mock(...) should simply replace the child component in test mode. However, it does not. The whole component is trying to render as is.
Here is one of the articles I was referring to: https://thoughtbot.com/blog/mocking-react-components-with-jest
Try moving the jest.mock call outside. I would say right at the top, just before and outside the describe section.
Jest needs to know about mocked component before it starts executing the test file in question.
The article which you reference, has this info,
Alternatively, you can put it within a __mocks__ folder next to the component if that is your preference.

How to mock store in redux toolkit

import React from 'react';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';
import { render, screen, fireEvent } from '#testing-library/react';
import MyApp from './MyApp ';
const initialState = {};
const mockStore = configureStore(initialState);
describe('<MyApp />', () => {
it('click button and shows modal', () => {
render(
<Provider store={mockStore}>
<MyApp />
</Provider>
);
fireEvent.click(screen.getByText('ADD MIOU'));
expect(queryByText('Add MIOU Setting')).toBeInTheDocument();
});
});
I am using jest and redux toolkit with reactjs, and trying to mock a store to write a test.
But got the following error
TypeError: store.getState is not a function
Is there any way to fix this? Am I missing something?
I assume you are trying to test a connected component, and you expect (1) action creators and reducers to be run and (2) redux state to be updated as part of your test?
I have not used redux-mock-store, but I see the following note on their documentation, which leads me to believe this library may not work the way you expect:
Please note that this library is designed to test the action-related logic, not the reducer-related one. In other words, it does not update the Redux store.
I suggest you try this approach for testing connected components. I have used this approach to write tests that update redux state and render connected components.
First, you override the RTL render method:
// test-utils.js
import React from 'react'
import { render as rtlRender } from '#testing-library/react'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
// Import your own reducer
import reducer from '../reducer'
function render(
ui,
{
initialState,
store = createStore(reducer, initialState),
...renderOptions
} = {}
) {
function Wrapper({ children }) {
return <Provider store={store}>{children}</Provider>
}
return rtlRender(ui, { wrapper: Wrapper, ...renderOptions })
}
// re-export everything
export * from '#testing-library/react'
// override render method
export { render }
Then you reference that new render method instead of RTL directly. You can also provide initial state for your test.
import React from 'react'
// We're using our own custom render function and not RTL's render
// our custom utils also re-export everything from RTL
// so we can import fireEvent and screen here as well
import { render, fireEvent, screen } from '../../test-utils'
import App from '../../containers/App'
it('Renders the connected app with initialState', () => {
render(<App />, { initialState: { user: 'Redux User' } })
expect(screen.getByText(/redux user/i)).toBeInTheDocument()
})
(All code copied from redux.js.org.)
I was having the same problem as you but thanks to #srk for linking the Redux docs and, the React Testing Library docs, I found a pretty good solution that worked for me with TypeScript:
// store.ts - just for better understanding
export const store = configureStore({
reducer: { user: userReducer },
})
export type RootState = ReturnType<typeof store.getState>
// test-utils.ts
import React, { ReactElement } from 'react'
import { Provider } from 'react-redux'
import { render as rtlRender, RenderOptions } from '#testing-library/react'
import {
configureStore,
EmptyObject,
EnhancedStore,
PreloadedState,
} from '#reduxjs/toolkit'
// import your reducers
import userReducer from 'features/user/user.slice'
import type { RootState } from 'app/store'
// ReducerTypes is just a grouping of each slice type,
// in this example i'm just passing down a User Reducer/State.
// With this, you can define the type for your store.
// The type of a configureStore() is called EnhancedStore,
// which in turn receives the store state as a generic (the same from store.getState()).
type ReducerTypes = Pick<RootState, 'user'>
type TStore = EnhancedStore<ReducerTypes>
type CustomRenderOptions = {
preloadedState?: PreloadedState<ReducerTypes & EmptyObject>
store?: TStore
} & Omit<RenderOptions, 'wrapper'>
function render(ui: ReactElement, options?: CustomRenderOptions) {
const { preloadedState } = options || {}
const store =
options?.store ||
configureStore({
reducer: {
user: userReducer,
},
preloadedState,
})
function Wrapper({ children }: { children: React.ReactNode }) {
return <Provider store={store}>{children}</Provider>
}
return rtlRender(ui, { wrapper: Wrapper, ...options })
}
// re-export everything
export * from '#testing-library/react'
// override render method
export { render }
Then you just have to pass down an object with the preloadedState property as the second parameter to your render; you can even define a new store inside the render if you want with the "store" property.
describe('[Component] Home', () => {
it('User not logged', () => {
const component = render(<Home />)
expect(component.getByText(/User is: undefined/i)).toBeInTheDocument()
})
it('User logged in', () => {
const component = render(<Home />, {
preloadedState: { user: { name: 'John' /* ...other user stuff */ } },
})
expect(component.getByText(/User is: John/i)).toBeInTheDocument()
})
})
I couldn't find anywhere else to paste my findings regarding redux toolkit and redux-mock-store.
In order to dispatch async thunks and test results you can specify the type of dispatch when creating the mock store.
import configureStore from 'redux-mock-store';
import { getDefaultMiddleware } from '#reduxjs/toolkit'
const middlewares = getDefaultMiddleware();
const mockStore = createMockStore<IRootState, AppDispatch>(middlewares);
describe('my thunk action', () => {
const store = mockStore();
test('calls my service', async() => {
await store.dispatch(myThunk({ id: 32 }));
expect(myService).toBeCalledWith({ id: 32 });
});
test('contains foo bar actions', async() => {
await store.dispatch(myThunk({ id: 32 }));
expect(store.getActions()).toEqual(....);
});
});
As of January 2023 it is no longer recommended to mock the store in redux, see the docs here and this answer for more information.

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

How to write unit test redux connected components in next.js app with Jest end Enzyme

In React Single Page App, we need to separate the logic of createStore to another component (usually called <Root />) to reuse it in your test file to let connect function link with the store
Root.js
import React from "react";
import { Provider } from "react-redux";
import { createStore } from "redux";
import reducers from "reducers";
import { applyMiddleware } from "redux";
import reduxPromise from "redux-promise";
const appliedMiddlewares = applyMiddleware(reduxPromise);
export default ({ children, initialState = {} }) => {
const store = createStore(reducers, initialState, appliedMiddlewares);
return <Provider store={store}>{children}</Provider>;
};
And then in your test file, to mount or shallow your component, your code should look like this:
import Root from "Root";
let mounted;
beforeEach(() => {
mounted = mount(
<Root>
<CommentBox />
</Root>
);
});
But for the case of Next.JS, the logic to let redux works with it was implemented in _app.js file, with some wrapper components (<Container>, <Component> ) that I do not know how it works so I could not find the way to separate the createStore logic
_app.js
import App, { Container } from "next/app";
import React from "react";
import Root from '../Root';
import withReduxStore from "../lib/with-redux-store";
import { Provider } from "react-redux";
class MyApp extends App {
render() {
const { Component, pageProps, reduxStore } = this.props;
return (
<Container>
<Provider store={reduxStore}>
<Component {...pageProps} />
</Provider>
</Container>
);
}
}
export default withReduxStore(MyApp);
Anyone knows it ? Many many thanks for helping me solve this.
Possibly I'm late adding a response, but this is what I did and worked!
First I imported the custom app:
import App from "../_app";
import configureStore from "redux-mock-store";
import thunk from "redux-thunk";
import { state } from "../../__mocks__/data";
const middlewares = [thunk];
const mockStore = configureStore(middlewares)(state);
Then I mocked the getInitialProps from the _app.js like:
const context = {
store: mockStore,
req: {
headers: {
cookie: ""
}
}
};
const props = await App.getInitialProps({
ctx: context,
Component: {
getInitialProps: jest.fn(() => {
return Promise.resolve({ ... });
})
}
});
Then, debugging over node_modules\next-redux-wrapper\src\index.tsx I noticed how the initialState must be set.
Then I added the following code:
delete window.__NEXT_REDUX_STORE__;
const wrapper = shallow(<App {...props} initialState={state}/>);
expect(toJson(wrapper)).toMatchSnapshot();
And run the tests, everything now works as expected.
If there is a cleaner solution please let me know.
I hope It works for you!

Why mapStateToProps do not map to the right prop?

I have this container
import { connect } from 'react-redux'
import { createType, getTypes } from '../modules/type'
import Type from '../components/Type'
const mapDispatchToProps = {
createType,
getTypes
}
const mapStateToProps = state => ({
types: state.type.types
})
export default connect(mapStateToProps, mapDispatchToProps)(Type)
and I would like to test it using enzyme. To do it, I'm using this test
import React from 'react'
import { Provider } from 'react-redux'
import { mount } from 'enzyme'
import TypeContainer from 'routes/Type/containers/TypeContainer'
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
const mockStore = configureMockStore([ thunk ]);
const mockStoreInitialized = mockStore({
type: {
types: [
{id: 1, name: 'type 1'}
]
}
});
describe.only('(Route) Type', () => {
it('should get container', () => {
const wrapper = mount(
<Provider store={mockStoreInitialized}>
<TypeContainer />
</Provider>
)
expect(wrapper.find(TypeContainer).prop('types')).to.deep.equal([{id: 1, name: 'type 1'}])
})
})
The test is failing (at the assertion level) because wrapper.find(TypeContainer).props() is empty. I can not find find why.
The strange thing is that if I check the coverage report, my test passed into the mapStateToProps function.
Did I missed something ?
TypeContainer won't have a prop called types, it will pull types from the store and pass it to Type, which will have a prop called types. So it's not that mapStateToProps is not doing the right thing; it's just you're making assertions against the wrong object.

Resources