最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to properly mock useSelector hook to return correct values while testing? - Stack Overflow

programmeradmin0浏览0评论

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 badges
Add a ment  | 

2 Answers 2

Reset to default 6

Relying 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.

发布评论

评论列表(0)

  1. 暂无评论