React组件测试:外部工具函数调用setState无效的解决方法
解决外部authValidator无法修改组件状态的问题
1. 确认authValidator的实现正确性
首先确保你的authValidator函数正确调用了传入的setState方法,示例实现如下:
// utils/authValidator.ts import { User } from "../types"; export const authValidator = ( user: User, setAuthValidationError: React.Dispatch<React.SetStateAction<string>> ): boolean => { // 验证逻辑:如果用户名或密码为空,设置错误信息并返回false if (!user.username || !user.password) { setAuthValidationError("Text about no password and username"); return false; } // 其他验证逻辑通过则返回true return true; };
2. 修正测试中的Mock逻辑
你的测试代码存在Mock对象不匹配的问题:你spyOn的是LoginComponent的authValidation方法,但组件实际调用的是外部导入的authValidator函数。需要正确Mock外部模块的函数:
方法一:使用jest.mock直接Mock整个模块
import { render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { BrowserRouter } from "react-router-dom"; import LoginComponent from "./LoginComponent"; import { authValidator } from "../../utils/authValidator"; // Mock authValidator函数 jest.mock("../../utils/authValidator", () => ({ authValidator: jest.fn() })); it("handles onClick", async () => { // 模拟authValidator的行为:调用传入的setState设置错误信息,返回false (authValidator as jest.Mock).mockImplementation((_, setAuthValidationError) => { setAuthValidationError("Text about no password and username"); return false; }); render(<BrowserRouter><LoginComponent /></BrowserRouter>); const loginButton = screen.getByText("Log in"); userEvent.click(loginButton); await waitFor(() => expect(authValidator).toHaveBeenCalledTimes(1)); await waitFor(() => expect(screen.getByText(/Text about no password and username/)).toBeInTheDocument()); });
方法二:使用jest.spyOnMock指定函数
import { render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { BrowserRouter } from "react-router-dom"; import LoginComponent from "./LoginComponent"; import * as authUtils from "../../utils/authValidator"; it("handles onClick", async () => { // 创建spy并模拟实现 const mockedAuthValidator = jest.spyOn(authUtils, "authValidator").mockImplementation((_, setAuthValidationError) => { setAuthValidationError("Text about no password and username"); return false; }); render(<BrowserRouter><LoginComponent /></BrowserRouter>); const loginButton = screen.getByText("Log in"); userEvent.click(loginButton); await waitFor(() => expect(mockedAuthValidator).toHaveBeenCalledTimes(1)); await waitFor(() => expect(screen.getByText(/Text about no password and username/)).toBeInTheDocument()); // 测试后恢复原函数(可选) mockedAuthValidator.mockRestore(); });
3. 检查组件调用逻辑
确保组件的handleSubmit方法正确传递了setAuthValidationError给authValidator,没有参数传递错误:
// LoginComponent.tsx import { authValidator } from "../../utils/authValidator"; import { useState, SyntheticEvent } from "react"; const LoginComponent = () => { const [user, setUser] = useState({ username: "", password: "" }); const [authValidationError, setAuthValidationError] = useState<string>(""); const handleSubmit = async (e: SyntheticEvent) => { e.preventDefault(); // 确认第二个参数是正确的setState函数 if (!authValidator(user, setAuthValidationError)) { return; } // 后续登录逻辑 }; // 组件渲染代码... }; export default LoginComponent;
内容的提问来源于stack exchange,提问作者Michal Zielarz
相关产品推荐
相关产品推荐

