Search code examples
javascriptreactjsreact-nativeaxiosreact-hooks-testing-library

Receiving a `globalObj.setTimeout is not a function` error for basic jest test


I am attempting to test a basic Axios hook and am receiving:

TypeError: globalObj.setTimeout is not a function

      at setImmediatePolyfill (node_modules/@testing-library/react-native/build/helpers/timers.js:59:20)
      at Object.then (node_modules/@testing-library/react-native/build/flushMicroTasks.js:26:32)

As far as I can tell the expected's are passing but am still getting this error for some reason which is failing the test. This is react native with the following package.json below. The hook works on my app, I'm assuming it has something to do with the testing library but I'm not sure what.

{
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web",
    "eject": "expo eject",
    "test": "jest"
  },
  "dependencies": {
    "@react-navigation/native": "^6.0.6",
    "@react-navigation/native-stack": "^6.2.5",
    "@testing-library/jest-native": "^4.0.4",
    "@testing-library/react-native": "^8.0.0",
    "axios": "^0.24.0",
    "eslint": "^8.2.0",
    "expo": "^43.0.2",
    "expo-status-bar": "^1.1.0",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-native": "^0.64.2",
    "react-native-collapsible": "^1.6.0",
    "react-native-safe-area-context": "^3.3.2",
    "react-native-screens": "^3.9.0",
    "react-native-web": "^0.17.5"
  },
  "devDependencies": {
    "@babel/core": "^7.16.0",
    "jest-expo": "^43.0.1",
    "miragejs": "^0.1.42",
    "react-hooks-testing-library": "^0.6.0",
    "react-test-renderer": "^17.0.2",
    "react-timer-mixin": "^0.13.4",
    "xmlhttprequest": "^1.8.0",
    "yarn-upgrade-all": "^0.5.4"
  },
  "private": true,
  "jest": {
    "preset": "react-native"
  }
}

My Axios hook:

// ./src/hooks.js

import { useState, useEffect } from "react";

export const useAPI = (apiFunction, params) => {
    const [data, setData] = useState(null);
    const [isLoading, setIsLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        apiFunction(params)
            .then(({data}) => {
                setData(data);
                setIsLoading(false);
            })
            .catch((err) => {
                setError("Something went wrong!");
                setIsLoading(false);
            });
    }, [apiFunction, params]);

    return [isLoading, data, error];
};

My test:

import { renderHook } from "@testing-library/react-hooks";
import { act } from "@testing-library/react-native";
import axios from "axios";
import { useAPI } from "../../Hooks/useAPI";

jest.mock("axios");
const mockData = {data: [{"test":"test"}]};


const getTestData = () => axios.get("/testEndpoint");

describe("fetch api data", () => {
    describe("on success", () => {
        it("should set data to test data and set isLoading false", async () => {
            axios.get.mockResolvedValueOnce(mockData);
            const {result, waitForNextUpdate } = renderHook(() => useAPI(getTestData));

            await act( async () => {
                await waitForNextUpdate();
            })

            const {data: expectedData} = mockData;

            expect(result.current[0]).toEqual(false);
            expect(result.current[1]).toEqual(expectedData);
            expect(result.current[2]).toEqual(null);
        })
    })
})

Solution

  • Figured out my own issue. I had set global.window = {} in the jest.setup.js file earlier. @testing-library/react-native uses a timers.js file in which they refer to globalObj which they obtain from the following: const globalObj = typeof window === 'undefined' ? global : window;. As window was not technically undefined, globalObj was being set to {} and did not have access to global functionality.