如何正确测试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)
问题分析与解决方法
错误根源
- 直接调用Hook违反规则:React Hook必须在组件或其他Hook的渲染流程中调用,直接在测试函数中调用会导致Hook执行环境异常,mock的
useState行为不符合预期。 - 语法错误:测试代码中构造
mockUser时多写了一个],导致对象结构无效。 - 未处理异步逻辑:Hook内部的
useEffect包含异步操作,直接断言无法捕获状态更新后的最终结果。 - 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
相关产品推荐
相关产品推荐

