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

如何在Jest中测试含异步函数的useEffect Hook?

问题分析与解决方案

错误原因

  1. 异步时机未处理:useEffect中的请求是异步执行的,测试时直接断言初始渲染的返回值,此时请求还未完成,拿到的是useRef的初始空数组。
  2. Ref更新不触发重渲染:用useRef存储请求结果,更新current值不会触发组件重渲染,测试里的hook.current始终停留在初始值。
  3. 测试逻辑矛盾:测试用例描述是"should return empty array",但断言的却是期望返回{ set: { a: 'b' } },逻辑不匹配。

修正Hook代码

原Hook用useRef存储数据的方式不合理,应该改用useState触发组件重渲染,让外部感知数据变化:

import { useState, useEffect } from 'react';
import fetchJsonp from 'fetch-jsonp';
import { getConfig } from './your-config-path'; // 替换为实际路径

const FETCH_URL = 'your-actual-fetch-url'; // 替换为实际请求地址

export const useDAMProductImages = (imageId: string) => {
  const { app: { baseImgDomain } } = getConfig();
  const [images, setImages] = useState<string[]>([]);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const getProductImages = async () => {
      if (!imageId) return;
      try {
        const url = new URL(FETCH_URL);
        // 根据需求添加请求参数,比如url.searchParams.append('imageId', imageId);
        const res = await fetchJsonp(url.href, {
          jsonpCallbackFunction: 'callback',
        });
        const jsonData = await res.json();
        setImages(jsonData as string[]);
        setError(null);
      } catch (err) {
        setImages([]);
        setError('图片加载失败');
      }
    };

    getProductImages();
  }, [imageId]);

  return { images, error };
};

正确测试写法

使用waitFor等待异步操作完成,同时Mock依赖的getConfig函数:

import { renderHook, waitFor } from '@testing-library/react';
import { useDAMProductImages } from './useDAMProductImages';
import fetchJsonp from 'fetch-jsonp';

// Mock配置获取函数
jest.mock('./your-config-path', () => ({
  getConfig: () => ({
    app: { baseImgDomain: 'test-domain.com' }
  })
}));

// Mock fetch-jsonp
jest.mock('fetch-jsonp', () =>
  jest.fn().mockImplementation(() =>
    Promise.resolve({
      status: 200,
      json: () => Promise.resolve(['img1.jpg', 'img2.jpg']),
    }),
  ),
);

describe('useDAMProductImages', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('请求成功时返回正确的图片数组', async () => {
    const { result } = renderHook(() => useDAMProductImages('valid-id'));

    // 初始状态为空数组
    expect(result.current.images).toEqual([]);

    // 等待异步请求完成后断言
    await waitFor(() => {
      expect(result.current.images).toEqual(['img1.jpg', 'img2.jpg']);
      expect(result.current.error).toBeNull();
    });

    // 验证请求被调用
    expect(fetchJsonp).toHaveBeenCalledTimes(1);
  });

  it('请求失败时返回错误状态', async () => {
    // 重写Mock模拟请求失败
    (fetchJsonp as jest.Mock).mockImplementation(() =>
      Promise.reject(new Error('Fetch error'))
    );

    const { result } = renderHook(() => useDAMProductImages('invalid-id'));

    await waitFor(() => {
      expect(result.current.images).toEqual([]);
      expect(result.current.error).toBe('图片加载失败');
    });
  });

  it('当imageId为空时不发起请求', () => {
    renderHook(() => useDAMProductImages(''));
    expect(fetchJsonp).not.toHaveBeenCalled();
  });
});

测试关键点

  • 用waitFor等待异步状态更新,避免过早断言。
  • Mock所有外部依赖(getConfig、fetchJsonp),确保测试环境独立。
  • 覆盖多种场景:请求成功、失败、无有效imageId的情况。

内容的提问来源于stack exchange,提问作者BlackWhite

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 14:21:31