React Testing Library测试React登录组件失败:useState钩子与异步Axios登录函数测试问题求助
解决React登录组件测试失败问题
我来帮你逐一排查并解决测试中的问题,先梳理下你当前遇到的核心错误根源,再给出修正后的完整测试方案:
核心错误分析
- 错误的Spy目标:你尝试监听
React.useState并命名为signIn,但组件内的signIn是自定义的表单提交函数,两者完全无关,导致toHaveBeenCalledTimes(1)始终返回0。 - DOM操作参数错误:
userEvent.type('email', 'abc@yahoo.com')中的第一个参数应该是DOM元素,而非字符串,导致输入未触发状态更新。 - 异步断言处理不当:未使用
waitFor等待Axios请求和状态更新完成,断言过早执行导致失败。 - 组件逻辑与测试不匹配:你的
Login组件本身不会渲染"welcome"文本,需在父组件Index中验证登录后的页面切换逻辑。
修正后的完整测试代码
测试文件基础配置
/** * @jest-environment jsdom */ import React from 'react'; import { screen, render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import Login from "../components/auth/login" import Index from "../components/index" import { userContext } from "../utils/context/UserContext" import axios from 'axios'; import Cookies from 'js-cookie'; // Mock第三方依赖 jest.mock('axios'); jest.mock('js-cookie', () => ({ get: jest.fn(() => 'mock-csrf-token'), })); // 上下文Mock值 const mockSetIsLoggedIn = jest.fn(); const mockSetUser = jest.fn(); const mockUser = {}; const mockIsLoggedIn = false; describe("Login Component Tests", () => { afterEach(() => { jest.clearAllMocks(); });
测试1:验证signIn触发Axios请求及状态更新
test('triggers signIn, calls Axios post, and updates context state on success', async () => { // 模拟Axios成功响应 const mockUserResponse = { user: { name: 'Rony', email: 'rony@example.com' } }; axios.post.mockResolvedValue({ status: 200, data: mockUserResponse }); // 渲染Login组件并传入上下文 render( <userContext.Provider value={{ user: mockUser, isLoggedIn: mockIsLoggedIn, setIsLoggedIn: mockSetIsLoggedIn, setUser: mockSetUser }}> <Login /> </userContext.Provider> ); // 获取表单元素 const emailInput = screen.getByLabelText(/email address/i); const passwordInput = screen.getByLabelText(/password/i); const submitButton = screen.getByRole('button', { name: /submit/i }); // 填写表单并提交 await userEvent.type(emailInput, 'abc@yahoo.com'); await userEvent.type(passwordInput, 'abcddef'); await userEvent.click(submitButton); // 验证Axios调用 expect(axios.post).toHaveBeenCalledTimes(1); expect(axios.post).toHaveBeenCalledWith( "/api/v1/sessions", { user: { email: 'abc@yahoo.com', password: 'abcddef' } }, expect.objectContaining({ headers: expect.any(Object) }) ); // 验证上下文状态更新 await waitFor(() => { expect(mockSetIsLoggedIn).toHaveBeenCalledWith(true); expect(mockSetUser).toHaveBeenCalledWith(mockUserResponse.user); }); });
测试2:验证useState值被正确设置
test('updates loginData state when input values change', async () => { render( <userContext.Provider value={{ user: mockUser, isLoggedIn: mockIsLoggedIn, setIsLoggedIn: mockSetIsLoggedIn, setUser: mockSetUser }}> <Login /> </userContext.Provider> ); const emailInput = screen.getByLabelText(/email address/i); const passwordInput = screen.getByLabelText(/password/i); // 验证邮箱输入 await userEvent.type(emailInput, 'test@example.com'); expect(emailInput).toHaveValue('test@example.com'); // 验证密码输入 await userEvent.type(passwordInput, 'testpassword'); expect(passwordInput).toHaveValue('testpassword'); });
测试3:验证登录成功后渲染Welcome文本
注意:需要先在Index组件的根路由添加欢迎文本渲染逻辑,比如:
// Index.js中修改根Route <Route exact path='/'> {isLoggedIn ? <h1>Welcome {user.name}!</h1> : <p>Please login</p>} </Route>
然后编写测试:
test('renders welcome text after successful login', async () => { // 模拟Axios成功响应 const mockUserResponse = { user: { name: 'Rony', email: 'rony@example.com' } }; axios.post.mockResolvedValue({ status: 200, data: mockUserResponse }); // 渲染Index组件(包含路由和上下文) render(<Index />); // 导航到登录页 await userEvent.click(screen.getByRole('link', { name: /login/i })); // 填写并提交表单 const emailInput = screen.getByLabelText(/email address/i); const passwordInput = screen.getByLabelText(/password/i); const submitButton = screen.getByRole('button', { name: /submit/i }); await userEvent.type(emailInput, 'abc@yahoo.com'); await userEvent.type(passwordInput, 'abcddef'); await userEvent.click(submitButton); // 等待欢迎文本出现 await waitFor(() => { expect(screen.getByText(/welcome/i)).toBeInTheDocument(); }); }); });
额外问题解决:"Unable to find the window object"错误
这个错误通常是JSDOM环境配置或依赖调用时机导致的,按以下方式解决:
- 确保测试文件顶部添加了
/** @jest-environment jsdom */ - 使用
jest.mock模拟依赖window的库(如js-cookie) - 避免手动包裹
act,React Testing Library的userEvent和render会自动处理异步更新
内容的提问来源于stack exchange,提问作者brg
相关产品推荐
相关产品推荐

