使用Vitest和React Testing Library测试React Hook遇问题
测试useAnalytics Hook的问题与解决方案
问题背景
我正在用Vitest和React Testing Library测试React Hook useAnalytics,该Hook通过useEffect加载外部脚本,测试目标是验证Hook能正常初始化并完成脚本加载。已Mock了Hook内部依赖的useLocale,现有测试代码如下:
import React from 'react'; import { vi, describe, it } from 'vitest'; import { useAnalytics } from './useAnalytics'; vi.mock('@hooks/useLocale', () => ({ useLocale: vi.fn(() => 'en') })); vi.spyOn(React, 'useEffect'); describe('useAnalytics', () => { it('render useAnalytics', () => { // downloadAppEvent is one of the function that this hook returns. const { downloadAppEvent } = useAnalytics(); }); });
尝试用renderHook(() => useAnalytics())测试时,出现ReferenceError: document is not defined错误,推测是Next.js服务端渲染环境导致的。
解决方案
1. 解决document is not defined问题
Next.js默认测试环境为Node.js(无浏览器DOM),需用jsdom模拟浏览器环境:
- 安装
jsdom依赖:
npm install -D jsdom
- 修改Vitest配置文件(
vitest.config.ts/vitest.config.js),启用DOM环境:
import { defineConfig } from 'vitest/config'; import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [react()], test: { environment: 'jsdom', globals: true, // 可选,避免重复导入describe/it等测试API }, });
2. 完善测试逻辑,验证脚本加载
配置好DOM环境后,用renderHook测试,并模拟脚本创建、加载流程,验证Hook行为:
import React from 'react'; import { vi, describe, it, expect } from 'vitest'; import { renderHook } from '@testing-library/react'; import { useAnalytics } from './useAnalytics'; vi.mock('@hooks/useLocale', () => ({ useLocale: vi.fn(() => 'en') })); describe('useAnalytics', () => { it('should initialize and load the analytics script', () => { // 模拟script元素及加载事件 const mockScript = { src: '', async: true, onload: null, }; const createElementSpy = vi.spyOn(document, 'createElement').mockReturnValue(mockScript as unknown as HTMLElement); const appendChildSpy = vi.spyOn(document.head, 'appendChild'); // 渲染Hook renderHook(() => useAnalytics()); // 验证script元素创建逻辑 expect(createElementSpy).toHaveBeenCalledWith('script'); expect(mockScript.src).toBe('你的分析脚本实际URL'); // 替换为Hook中加载的脚本地址 expect(mockScript.async).toBe(true); expect(appendChildSpy).toHaveBeenCalledWith(mockScript); // 触发脚本加载完成事件,验证后续逻辑(若有) if (typeof mockScript.onload === 'function') { mockScript.onload(); // 可添加脚本加载完成后的断言,比如分析实例是否初始化 } // 恢复原生方法 createElementSpy.mockRestore(); appendChildSpy.mockRestore(); }); it('should return expected functions', () => { const { result } = renderHook(() => useAnalytics()); expect(typeof result.current.downloadAppEvent).toBe('function'); }); });
3. 异步逻辑测试注意事项
若脚本加载涉及异步初始化,可使用waitFor等待断言条件满足:
import { waitFor } from '@testing-library/react'; // 在测试中使用 await waitFor(() => { expect(/* 异步完成后的验证条件 */).toBe(true); });
内容的提问来源于stack exchange,提问作者Divyanshu Rawat
相关产品推荐
相关产品推荐

