How to test Async calls in JEST using request without mocking and awaits for react-hooks testing library

Viewed 953

I am not good at writing tests as far as non API functionalities are concerned I have tested using JEST with the help of rendererHook Just like below Sample correct TEST:

import { render, screen, cleanup } from '@testing-library/react';
import useAppDrawer from "../../../utils/hooks/useAppDrawer";
import {act,renderHook} from '@testing-library/react-hooks';
import React from "react";

describe("hook: useAppDrawer", () => {

    afterEach(() => {
        cleanup();
    });

    test('drawer open', () => {
        const {result} = renderHook(useAppDrawer);

        act(()=>{
            result.current.handleDrawerOpen();
        })
        expect(result.current.open).toBe(true);

    });
    test('drawer close', () => {
        const {result} = renderHook(useAppDrawer);

        act(()=>{
            result.current.handleDrawerClose();
        })
        expect(result.current.open).toBe(false);

    });

});

But what will we do in case of API calls in JEST that is still confusing to me. I am still not able to understand that how can I test API calls in JEST like this below Problematic function:

import React, {useContext} from "react";
import request from "../services/Http";
import useIsLoading from "./useIsLoading";
import {Context} from "../store/context/store";
import {SET_BOOTHS} from "../store/context/Constants";

export default function useFetchBooths(){

    const {isLoading, setIsLoading} = useIsLoading()
    const [{booths}, dispatch] = useContext(Context)

    function fetchBooths(){

        setIsLoading(true)

        request.get('/event/get-booth-list')
            .then((res) => {
                setIsLoading(false)
                if (res)
                {
                    dispatch({
                        type: SET_BOOTHS,
                        payload: res.data.booth_data
                    })
                }
            })
    }

    function searchBooths(value){

        if (value)
        {
            let obj = booths.filter(booth => booth.booth_name.toLowerCase().includes(value.toLowerCase()));

            if (obj.length > 0)
            {
                dispatch({
                    type: SET_BOOTHS,
                    payload: obj
                })
            }
            else
            {
                fetchBooths()
            }

        }
        else
        {
            fetchBooths()
        }
    }

    return { booths, fetchBooths, isLoading, searchBooths }
}

what would be the possible jest test for the above function? considering cases like if booth is fetched or not as in both cases I am getting booth value to be []. My attempt so far for this is:

import { render, screen, cleanup, waitFor } from '@testing-library/react';
import useFetchBooths from "../../../utils/hooks/useFetchBooths";
import {act,renderHook} from '@testing-library/react-hooks';
import React from "react";
import * as requestsModule from "../../../utils/services/Http";
import {Store} from "../../../utils/store/context/store";

describe("hook: useFetchBooths", () => {

    afterEach(() => {
        cleanup();
    });

    test('Booth is fetched', async () => {
        const boothData = [
            {
                "message": "Data fetched Successfully",
                "success": true
            }
        ];
        const wrapper = ({children}) => (
            <Store>{children}</Store>
        )
        const {result } = renderHook(() => useFetchBooths(), {wrapper});
         jest.spyOn(result.current, "fetchBooths").mockResolvedValue(boothData);
         console.log(result.current.fetchBooths())
        await expect(result.current.fetchBooths()).resolves.toEqual([{ "success": true, "message": "Data fetched Successfully" }]);

    });

});

The above test case is passed, but this is a mocked API(I am creating data and comparing it with my own written data but this is not I want) but I want to ensure my real API is working or not in that test like I am fetching booth list in fetchBooths() function so how can I test it with mock API when my everything is dependant on real API I want to test it by checking my real API is actually working or not in that test by using some async/await But I am badly stuck at this point. Also, I am unable to understand what would be the test case for searchBooths function as it is appearing undefined and booths list appearing as [ ] although it shouldn't be [] I am still not able to figure out the solution. Please help me write these test cases

1 Answers

In your test code use a spy to mock the requests module:

import * as requestsModule from "../services/Http";

describe("hook: useFetchBooths", () => {

    afterEach(() => {
        cleanup();
    });

    test('Booth is fetched', async () => {

        jest.spyOn(requestsModule, 'get').mockResolvedValue([]) // <-- here you should provide the data you want your hook to see.

        const wrapper = ({children}) => (
            <Store>{children}</Store>
        )

        const {result } = renderHook(() => useFetchBooths(), {wrapper});
        await act(async () => {
            await waitFor(() => result.current.fetchBooths() )
        })

        await act(async () => {
            await waitFor(() => result.current.booths )
            console.log(result.current);
        })
        expect(result.current.booths).toEqual([]);

    });

});

Update: how to mock useContext

  1. create a custom hook
// boothsContext.js

import {Context} from "../store/context/store";

export const useBoothsContext = () => useContext(Context)

  1. mock the hook using jest.spyOn:
import { wait } from '@testing-library/react'
import * as BoothsContextModule from './boothsContext.js'

...

test('Booth is fetched', async () => {
    const mockDispatch = jest.fn()
    const data = { booths: []} // change this to inject different data into your hook result
    jest.spyOn(BoothsContextModule, 'useBoothsContext')
        .mockReturnValue([data, mockDispatch])

    // perform test steps

   await wait(() => expect(mockDispatch).toHaveBeenCalledWith({
      type: SET_BOOTHS,
      payload: {...} // <-- expected payload you want to assert
   }))

Related