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

如何为React类组件login方法中的fetch编写Jest mock函数?

用Jest Mock Fetch并测试React登录组件的完整方案

找了好久没找到可行的方案?别着急,我来一步步帮你实现针对这个Login组件的测试,覆盖登录成功和失败的核心场景。

第一步:全局Mock Fetch函数

你的组件直接使用了浏览器全局的fetch,在Jest测试环境中,我们需要先mock这个全局方法,才能模拟接口返回的不同场景。可以用jest.spyOn来临时替换global.fetch,测试完成后再恢复原始实现。

第二步:编写组件集成测试(推荐)

我们用@testing-library/react模拟真实用户交互,这样测试更贴近实际使用场景。先确保你已经安装了必要依赖:

npm install --save-dev @testing-library/react @testing-library/jest-dom jest

测试登录成功场景

这个用例会模拟用户输入正确账号密码、点击登录,验证fetch调用、localStorage存储和Redux状态变化:

import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { Provider } from 'react-redux';
import { store } from '../../store';
import Login from './Login';

describe('Login Component - 登录成功场景', () => {
  beforeEach(() => {
    // 每次测试前清空localStorage并重置fetch mock
    localStorage.clear();
    jest.spyOn(global, 'fetch').mockResolvedValue({
      text: () => Promise.resolve('fake-jwt-token'), // 模拟返回JWT令牌
    });
  });

  afterEach(() => {
    // 测试后恢复fetch的原始实现
    global.fetch.mockRestore();
  });

  it('成功登录后应存储token并跳转到搜索页', async () => {
    // 用Redux Provider包裹组件,确保状态能正常传递
    render(
      <Provider store={store}>
        <Login />
      </Provider>
    );

    // 模拟用户输入账号密码
    const emailInput = screen.getByPlaceholderText('Email address');
    const passwordInput = screen.getByPlaceholderText('Password');
    fireEvent.change(emailInput, { target: { value: 'test@example.com' } });
    fireEvent.change(passwordInput, { target: { value: 'password123' } });

    // 模拟点击登录按钮
    const loginButton = screen.getByText('Sign in');
    fireEvent.click(loginButton);

    // 验证fetch是否被正确调用
    expect(global.fetch).toHaveBeenCalledWith(
      'https://reqres.in/api/login',
      expect.objectContaining({
        method: 'POST',
        headers: expect.any(Headers),
        body: JSON.stringify({
          email: 'test@example.com',
          password: 'password123',
        }),
      })
    );

    // 等待异步操作完成,检查Redux状态更新
    await waitFor(() => {
      const state = store.getState();
      expect(state.login.isLoggedIn).toBe(true);
    });

    // 验证localStorage是否存储了token
    const storedLogin = localStorage.getItem('login');
    expect(JSON.parse(storedLogin)).toEqual({ token: 'fake-jwt-token' });

    // 验证是否跳转到搜索页
    expect(screen.queryByText('Sign in')).not.toBeInTheDocument();
  });
});

测试登录失败场景

这个用例会模拟用户输入错误信息,验证Redux状态设置为未登录、localStorage无存储:

describe('Login Component - 登录失败场景', () => {
  beforeEach(() => {
    localStorage.clear();
    jest.spyOn(global, 'fetch').mockResolvedValue({
      text: () => Promise.resolve('{"error":"user not found"}'), // 模拟返回错误信息
    });
  });

  afterEach(() => {
    global.fetch.mockRestore();
  });

  it('登录失败后应设置未登录状态', async () => {
    render(
      <Provider store={store}>
        <Login />
      </Provider>
    );

    // 模拟输入错误账号密码
    const emailInput = screen.getByPlaceholderText('Email address');
    const passwordInput = screen.getByPlaceholderText('Password');
    fireEvent.change(emailInput, { target: { value: 'wrong@example.com' } });
    fireEvent.change(passwordInput, { target: { value: 'wrongpassword' } });

    // 点击登录按钮
    const loginButton = screen.getByText('Sign in');
    fireEvent.click(loginButton);

    // 等待异步操作完成,验证状态
    await waitFor(() => {
      const state = store.getState();
      expect(state.login.isLoggedIn).toBe(false);
    });

    // 验证localStorage无登录信息
    expect(localStorage.getItem('login')).toBeNull();

    // 验证登录页面仍存在
    expect(screen.getByText('Sign in')).toBeInTheDocument();
  });
});

额外:单独测试login类方法(可选)

如果你想单独测试login方法而不通过组件交互,可以直接mock类的原型方法:

import { loginFunc } from './Login';

describe('Login类的login方法', () => {
  it('应调用fetch并传入正确参数', () => {
    jest.spyOn(global, 'fetch').mockResolvedValue({ text: () => Promise.resolve('fake-token') });
    
    // 创建mock的上下文对象,模拟组件的this
    const mockThis = {
      state: { username: 'test@example.com', password: 'password123' },
      props: { dispatch: jest.fn() },
    };

    // 调用login方法并绑定mock的this
    loginFunc.call(mockThis);

    // 验证fetch调用参数
    expect(global.fetch).toHaveBeenCalledWith(
      'https://reqres.in/api/login',
      expect.objectContaining({
        method: 'POST',
        body: JSON.stringify({ email: 'test@example.com', password: 'password123' }),
      })
    );
  });
});

这样就能完整覆盖你需要的mock fetch和测试场景了,每个用例都验证了关键行为:fetch调用是否正确、状态是否更新、存储是否符合预期。

内容的提问来源于stack exchange,提问作者Hrishikesh D Kakkad

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 13:07:42