You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 12:40:30