使用Vitest测试useLocalTime Hook时renderHook返回null的问题
测试useLocalTime Hook时renderHook返回null的原因及解决办法
我用Vitest测试自定义React Hook useLocalTime,但@testing-library/react提供的renderHook函数始终返回null,希望排查原因。
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; };
测试代码
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"); }); });
报错信息
AssertionError: expected null to deeply equal '16 June 2022 14:49:35' - Expected: "16 June 2022 14:49:35" + Received: null
原因分析
- 初始状态为null:Hook的
formattedTime初始值设为null,而useEffect内部仅通过setInterval设置了1秒后执行的更新逻辑,没有立即触发时间格式化操作。 - 测试时机过早:测试代码在
renderHook后立刻断言,此时useEffect还没完成首次执行,定时器也未触发,result.current仍保持初始的null。
解决办法
方法一:修改Hook,初始化时立即执行格式化
在useEffect中先手动调用一次updateTime,确保Hook首次渲染就返回格式化后的时间:
useEffect(() => { const updateTime = () => { const currentTime = dayJsObject.format(format ?? "D MMMM YYYY LTS"); setFormattedTime(capitalizeFirstLetters(currentTime)); }; updateTime(); // 立即执行一次,初始化状态 const interval = setInterval(updateTime, 1000); return () => clearInterval(interval); }, [dayJsObject, format]);
方法二:修改测试代码,等待状态更新
使用@testing-library/react的waitFor工具,等待状态完成更新后再断言:
import {renderHook, waitFor} from "@testing-library/react"; import dayjs from "dayjs"; import {useLocalTime} from "ui/hooks/useLocalTime"; describe("useLocalTime", () => { it("should return the formatted time", async () => { const dayJsObject = dayjs("2022-06-16T14:49:35.718Z"); const {result} = renderHook(() => useLocalTime({dayJsObject})); await waitFor(() => { expect(result.current).toEqual("16 June 2022 14:49:35"); }); }); });
方法三:使用虚拟定时器快进时间(Vitest)
借助Vitest的虚拟定时器功能,直接快进1秒触发定时器逻辑:
import {renderHook} from "@testing-library/react"; import dayjs from "dayjs"; import {useLocalTime} from "ui/hooks/useLocalTime"; describe("useLocalTime", () => { it("should return the formatted time", () => { vi.useFakeTimers(); // 启用虚拟定时器 const dayJsObject = dayjs("2022-06-16T14:49:35.718Z"); const {result} = renderHook(() => useLocalTime({dayJsObject})); vi.advanceTimersByTime(1000); // 快进1秒 expect(result.current).toEqual("16 June 2022 14:49:35"); vi.useRealTimers(); // 恢复真实定时器 }); });
内容的提问来源于stack exchange,提问作者Johan
相关产品推荐
相关产品推荐

