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

如何正确测试React TypeScript的useIsCompanyLocked自定义Hook?

如何正确测试React TypeScript自定义Hook useIsCompanyLocked?

我在测试一个React TypeScript自定义Hook useIsCompanyLocked,但无论尝试何种写法都会报错,求正确的测试方式。

自定义Hook代码

import { useEffect, useState } from 'react';
import { useUser } from '.';

/**
 * Check If the company is locked
 */
export const useIsCompanyLocked = (): boolean => {
  const userInfo = useUser();
  const [status, setStatus] = useState(false);

  useEffect(() => {
    if (!userInfo) {
      return;
    }

    (async (): Promise<void> => {
      setStatus(false);
      // Do some stuff
      setStatus('do some condition which returns a boolean result');
    })();
  }, [userInfo, status]);

  return status;
};

失败的测试代码

/**
 * @jest-environment jsdom
 */
import { useIsCompanyLocked } from '../useIsCompanyLocked';
import { mockUseUser } from '@myTest/test-utilities';
import React from 'react';

// eslint-disable-next-line @typescript-eslint/no-var-requires
const appUtils = require('@myTest/app-utilities');

describe('useUserHasSecurityPermissionForCompany', () => {
  const setStatus = jest.fn();
  let useStateSpy: any;

  beforeEach(() => {
    useStateSpy = jest.spyOn(React, 'useState');
    useStateSpy.mockImplementation((initialState: any) => [initialState, setStatus]);
  });

  it('should return "false" if user is Admin', () => {
    let mockUser = mockUseUser();
    mockUser = {
      ...mockUser,
      isAdmin: true
      ] // 此处存在语法错误,应为}
    };
    jest.spyOn(appUtils, 'useUser').mockReturnValue(mockUser);
    const obj = useIsCompanyLocked();
    expect(obj).toEqual(false);
  });
});

错误信息

Summary of all failing tests
FAIL src/hooks/__tests__/useIsCompanyLocked.test.tsx (10.631s)
  ● useIsCompanyLocked › should return "false" if user is Admin

    expect(received).toEqual(expected) // deep equality

    Expected: false
    Received: [false, [Function mockConstructor]]

      39 |     jest.spyOn(appUtils, 'useUser').mockReturnValue(mockUser);
      40 |     const obj = useIsCompanyLocked();
    > 41 |     expect(obj).toEqual(false);
         |                 ^
      42 |   });
      43 |

      at Object.<anonymous> (src/hooks/__tests__/useIsCompanyLocked.test.tsx:41:17)

问题分析与解决方法

错误根源

  1. 直接调用Hook违反规则:React Hook必须在组件或其他Hook的渲染流程中调用,直接在测试函数中调用会导致Hook执行环境异常,mock的useState行为不符合预期。
  2. 语法错误:测试代码中构造mockUser时多写了一个],导致对象结构无效。
  3. 未处理异步逻辑:Hook内部的useEffect包含异步操作,直接断言无法捕获状态更新后的最终结果。
  4. Hook潜在bug:useEffect的依赖数组包含status,会导致setStatus触发后重新执行useEffect,大概率造成无限循环,需调整依赖为[userInfo]。

正确测试代码

使用@testing-library/react的renderHook工具测试Hook,这是React官方推荐的Hook测试方案:

/**
 * @jest-environment jsdom
 */
import { renderHook, waitFor } from '@testing-library/react';
import { useIsCompanyLocked } from '../useIsCompanyLocked';
import { mockUseUser } from '@myTest/test-utilities';
import * as appUtils from '@myTest/app-utilities';

describe('useIsCompanyLocked', () => {
  // 每个测试前清理mock,避免跨用例污染
  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('should return false when user is Admin', async () => {
    // 构造管理员用户模拟数据
    const mockAdminUser = {
      ...mockUseUser(),
      isAdmin: true
    };
    // 模拟useUser Hook返回管理员用户
    jest.spyOn(appUtils, 'useUser').mockReturnValue(mockAdminUser);

    // 使用renderHook在React环境中渲染自定义Hook
    const { result } = renderHook(() => useIsCompanyLocked());

    // 断言初始状态为false
    expect(result.current).toBe(false);

    // 等待异步useEffect执行完成,捕获最终状态
    await waitFor(() => {
      // 根据实际业务逻辑调整断言,此处假设管理员对应公司未锁定
      expect(result.current).toBe(false);
    });
  });
});

关键说明

  • renderHook:提供标准的React渲染环境,让Hook正常执行,result.current会实时返回Hook的返回值。
  • waitFor:等待异步操作完成,确保状态更新后再执行断言,避免因异步时序导致的断言失败。
  • jest.clearAllMocks():在每个测试用例前清理mock,防止不同测试之间的状态污染。
  • 修复原Hook依赖问题:将useEffect的依赖数组从[userInfo, status]改为[userInfo],彻底避免无限循环风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 13:10:22