2

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?

1 Answer 1

3

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', () => {

Sign up to request clarification or add additional context in comments.

4 Comments

Hi, @Jakub. Could you help me again? I'm facing similar issue in other test file. I'm also trying to mock useAuth and it doesn't throw error that time. However, the test fails as isAuthenticated returns false. I've posted it here stackoverflow.com/questions/71778604/…
@Jakub do you know how can I implement a different value for each test
@ChristianHerrejon If you need different values, you need to use jest.fn() alongside mockImplementation. Comments are a limited place to describe it, but I'll try. Step 1: mock the default to be default:: jest.fn() instead of a function returning object. Step 2: import the mock import useAuth from './useAuth'. Step 3: mock the implementation with useAuth.mockReturnValue(true) or useAuth.mockReturnValue(false) inside the test. You can see it here: stackoverflow.com/a/45007792/2188587
Please have a look at this i am facing same problem : stackoverflow.com/questions/73977384/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.