Search code examples
reactjsreact-hooksreact-testing-libraryvitest

AssertionError: expected null to deeply equal XXX


I use vitest to test my components and hooks.

I have a useLocalTime hook that I want to test, but the renderHook function from @testing-library/react always returns null.

Any idea why ?

Here is the hook :

import {useEffect, useState} from "react";
import {Dayjs} from "dayjs";

interface Props {
    dayJsObject: Dayjs;
    format?: string;
}

const capitalizeFirstLetters = (text: string) => {
    return text
        .toLowerCase()
        .split(" ")
        .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
        .join(" ");
};

export const useLocalTime = ({dayJsObject, format}: Props) => {
    const [formattedTime, setFormattedTime] = useState<string | null>(null);

    useEffect(() => {
        const updateTime = () => {
            const currentTime = dayJsObject.format(format ?? "D MMMM YYYY LTS");

            setFormattedTime(capitalizeFirstLetters(currentTime));
        };

        const interval = setInterval(updateTime, 1000);

        return () => clearInterval(interval);
    }, [dayJsObject, format]);

    return formattedTime;
};

Here is the test :

import {renderHook} from "@testing-library/react";
import dayjs from "dayjs";
import {useLocalTime} from "ui/hooks/useLocalTime";

describe("useLocalTime", () => {
    it("should return the formatted time", () => {
        const dayJsObject = dayjs("2022-06-16T14:49:35.718Z");
        const {result} = renderHook(() => useLocalTime({dayJsObject}));

        expect(result.current).toEqual("16 June 2022 14:49:35");
    });
});

Here is the error :

AssertionError: expected null to deeply equal '16 June 2022 14:49:35'

- Expected: 
"16 June 2022 14:49:35"

+ Received: 
null

Solution

  • That's because updateTime in your useLocalTime hook isn't firing due to it being in an 1000ms interval. It's returning the default state (null) provided in the hook.

    1. You need to use fake timers simulate the interval and have your test as such
    describe("useLocalTime", () => {
        it("should return the formatted time", () => {
            jest.useFakeTimers();
            const dayJsObject = dayjs("2022-06-16T14:49:35.718Z");
            const {result} = renderHook(() => useLocalTime({dayJsObject}));
            act(() => {
             jest.advanceTimersByTime(1100);
            });
            expect(result.current).toEqual("16 June 2022 14:49:35");
        });
    });
    
    1. Or instead you can update the hook to call updateTime on initial load.