I have a function that calls useSelector multiple times upon rendering. In mocking the selector I used:
jest.spyOn(Redux, 'useSelector').mockReturnValueOnce(data).mockReturnValueOnce(moreData);
This changes the order of how hooks are called in my ponent. I have also tried creating a mock store and sending that into rendered ponent in the test as such:
const state = { userGuid: 'testGuid', user };
const store = mockStore(state);
jest.spyOn(Redux, 'useSelector').mockImplementation(() => store.getState());
const { getByTestId } = wrappedRender(ProfileScreen, mockProps, { store });
But this wraps the data in an extra object which my ponent cannot de-structure.
As of now I cannot find any other way to mock the return values of multiple useSelector calls without changing the order of hooks called. Any help is appreciated.
I have a function that calls useSelector multiple times upon rendering. In mocking the selector I used:
jest.spyOn(Redux, 'useSelector').mockReturnValueOnce(data).mockReturnValueOnce(moreData);
This changes the order of how hooks are called in my ponent. I have also tried creating a mock store and sending that into rendered ponent in the test as such:
const state = { userGuid: 'testGuid', user };
const store = mockStore(state);
jest.spyOn(Redux, 'useSelector').mockImplementation(() => store.getState());
const { getByTestId } = wrappedRender(ProfileScreen, mockProps, { store });
But this wraps the data in an extra object which my ponent cannot de-structure.
As of now I cannot find any other way to mock the return values of multiple useSelector calls without changing the order of hooks called. Any help is appreciated.
Share Improve this question edited Oct 2, 2020 at 16:50 Drew Reese 204k18 gold badges240 silver badges271 bronze badges asked Oct 2, 2020 at 16:32 JacobJacob 3013 silver badges13 bronze badges2 Answers
Reset to default 6Relying on mockReturnValueOnce
calls multiple times is a no-go as the order might change. The code below will just return the stubbed state no matter how many times useSelector
is being called.
import * as redux from 'react-redux'
const user = {
id: 1,
name: 'User',
}
const state = { user }
jest
.spyOn(redux, 'useSelector')
.mockImplementation((callback) => callback(state))
you shouldn't mock useSelector and useDispatch as eplained in the redux docs here: https://redux.js/usage/writing-tests… but you could control the intialstate pased in to store in your test or dispatch action in your test which should be more desirable… and this is how i implemented it following the docs.
in store/store.js i created a function that returns a configure store function so i can pass in an initial state for the store
import { configureStore, bineReducers } from "@reduxjs/toolkit";
import { reducer } from "./yourSlice";
import { reducerTwo } from "./anotherSlice";
const rootReducer = bineReducers({
authState: reducer,
mode: reducerTwo
});
export default function setUpStore(preloadedState) {
return configureStore({ reducer: rootReducer, preloadedState });
}
in utils/RTKOveride.js
import { render } from "@testing-library/react";
import { Provider } from "react-redux";
import setUpStore from "store/store";
function renderWithWrapers(ui,
{
preloadedState = {},
store = setUpStore(preloadedState),
...options
} = {},
) {
function Wrapper({ children }) {
return (
<Provider store={store}>
{children}
</Provider>
);
}
return { store, ...render(ui, { wrapper: Wrapper, ...options }) };
}
export * from "@testing-library/react";
export { renderWithWrapers as render };
i overide the render function from RTK, wrapped my ponents that will be passed via the props.children in the provider ponent so ponents using useSelector and useDispatch still function properly. even if you do not pass in a store or preloadedState(initialState) the code wont break as a store is created everytime the renderedWithWrapper is called
NOTE: the official react testing tookit explains how you could overide the render method and pass in additional options
then in your test/Component.test.js
import { render, screen } from "utils/RtkOveride";
import SomeComponent from "../Component";
test("some foo test", () => {
render(
<SomeComponent />
, { preloadedState: { authState: { state: true, refresh: "", access: "" } } }
);
const inputBar = screen.queryByText("foo", { exact: false });
expect(inputBar).toBeInTheDocument();
});
So you could pass in an initialState to change the default Slice InitialState or follow the other way the redux docs detailed... Hopes this helps.