Home > Back-end >  How to mock react custom hook return value as a module with Jest
How to mock react custom hook return value as a module with Jest

Time:04-05

I need to mock my custom hook when unit testing React component. I've read some stackoverflow answers but haven't succeeded in implementing it correctly.

I can't use useAuth without mocking it as it depends on server request and I'm only writing unit tests at the moment.

//useAuth.js - custom hook

const authContext = createContext();

function useProvideAuth() {
    const [accessToken, setAccessToken] = useState('');
    const [isAuthenticated, setAuthenticated] = useState(
        accessToken ? true : false
    );

    useEffect(() => {
        refreshToken();
    }, []);

    const login = async (loginCredentials) => {
        const accessToken = await sendLoginRequest(loginCredentials);
        if (accessToken) {
            setAccessToken(accessToken);
            setAuthenticated(true);
        }
    };

    const logout = async () => {
        setAccessToken(null);
        setAuthenticated(false);
        await sendLogoutRequest();
    };

    const refreshToken = async () => {
        const accessToken = await sendRefreshRequest();
        if (accessToken) {
            setAccessToken(accessToken);
            setAuthenticated(true);
        } else setAuthenticated(false);
        setTimeout(async () => {
            refreshToken();
        }, 15 * 60000 - 1000);
    };

    return {
        isAuthenticated,
        accessToken,
        login,
        logout
    };
}

export function AuthProvider({ children }) {
    const auth = useProvideAuth();

    return <authContext.Provider value={auth}>{children}</authContext.Provider>;
}

AuthProvider.propTypes = {
    children: PropTypes.any
};

const useAuth = () => {
    return useContext(authContext);
};

export default useAuth;

The test I've written

//mainPage.test.js

import React from 'react';
import { render, screen } from '@testing-library/react';
import Main from '../main/mainPage';

describe('Main when !isAuthenticated', () => {
    beforeEach(() => {
        jest.mock('../auth/useAuth', () => {
            const originalModule = jest.requireActual('../auth/useAuth');
            return {
                __esModule: true,
                ...originalModule,
                default: () => ({
                    isAuthenticated: false,
                    login: jest.fn,
                    logout: jest.fn
                })
            };
        });
    });

    afterEach(() => {
        jest.resetModules();
    });

    it('displays image and login form', () => {
        render(<Main />);

        const image = screen.getByRole('img');
        const form = document.querySelector('[data-testid=loginForm]');
        expect(image).toBeInTheDocument();
        expect(form).toBeInTheDocument();
    });
});

However, I get this error.

 TypeError: Cannot read properties of undefined (reading 'isAuthenticated')

       7 |
       8 | function Main() {
    >  9 |      const isAuthenticated = useAuth().isAuthenticated;
         |                              ^
      10 |      const location = useLocation();
      11 |
      12 |      if (isAuthenticated)

      at Main (src/main/mainPage.js:9:26)
      at renderWithHooks (node_modules/react-dom/cjs/react-dom.development.js:14985:18)...

I've been also trying to use spyOn but nothing helped. What exactly do I need to change to make the mock work?

CodePudding user response:

The mocking should happen before any describe block:

import React from 'react';
import { render, screen } from '@testing-library/react';
import Main from '../main/mainPage';

jest.mock('../auth/useAuth', () => {
    const originalModule = jest.requireActual('../auth/useAuth');
    return {
        __esModule: true,
        ...originalModule,
        default: () => ({
            isAuthenticated: false,
            login: jest.fn,
            logout: jest.fn
        })
    };
});

describe('Main when !isAuthenticated', () => {

  • Related