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

如何使用Jest Mock fetch完成POST请求场景的单元测试?

如何使用Jest对fetch语句进行Mock?

现有登录组件代码如下:

export default function Login() {
  function LoginUser() {
    const requestOptions = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Accept: 'application/json',
      },
      body: JSON.stringify({ username: username, password: password }),
    };
    fetch('http://localhost:8080/post', requestOptions)
      .then((response) => {
        if (response.status === 200) {
          console.log('succesfull');
        } else {
          throw new Error('Invalid credentials');
        }
      })
      .catch((err) => {
        console.log(err);
      });
  }

  return (
    <Button
      block
      size='lg'
      type='submit'
      onClick={LoginUser}
      disabled={!validateForm()}
    >
      Login
    </Button>
  );
}

我们可以通过全局Mock fetch API、模拟不同请求返回值的方式完成单元测试,具体实现如下:

测试代码实现

首先安装必要依赖(已安装可跳过):React组件测试通常搭配@testing-library/react使用,Jest v28+版本需要额外安装jsdom环境模拟浏览器API。

测试文件完整代码:

import { render, screen, fireEvent } from '@testing-library/react';
import Login from './Login';

// 全局Mock浏览器fetch API
global.fetch = jest.fn();

// 每次测试执行前清空fetch的调用记录,避免用例之间互相影响
beforeEach(() => {
  fetch.mockClear();
});

// 测试场景1:登录请求返回200状态(登录成功)
test('登录成功时fetch参数符合预期,打印成功日志', async () => {
  // 模拟fetch返回200成功状态
  fetch.mockResolvedValueOnce({
    status: 200,
    json: () => Promise.resolve({}),
  });
  // Mock console.log验证输出结果
  const consoleSpy = jest.spyOn(console, 'log').mockImplementation();

  // 渲染登录组件
  render(<Login />);
  // 若组件内username、password为受控字段,可在此处先模拟用户输入账号密码:
  // fireEvent.change(screen.getByLabelText(/用户名/i), { target: { value: 'testUser' } })
  // fireEvent.change(screen.getByLabelText(/密码/i), { target: { value: 'testPwd' } })

  // 点击登录按钮触发请求
  const loginButton = screen.getByRole('button', { name: /login/i });
  fireEvent.click(loginButton);

  // 验证fetch调用参数是否正确
  expect(fetch).toHaveBeenCalledTimes(1);
  expect(fetch).toHaveBeenCalledWith(
    'http://localhost:8080/post',
    expect.objectContaining({
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Accept: 'application/json',
      },
      // 此处可替换为你实际传入的账号密码值
      body: JSON.stringify({ username: 'testUser', password: 'testPwd' })
    })
  );

  // 等待fetch异步逻辑执行完成
  await new Promise(process.nextTick);
  // 验证成功日志输出
  expect(consoleSpy).toHaveBeenCalledWith('succesfull');

  // 恢复console.log原始实现
  consoleSpy.mockRestore();
});

// 测试场景2:登录请求返回非200状态(登录失败)
test('登录失败时抛出错误,打印错误日志', async () => {
  // 模拟fetch返回401失败状态
  fetch.mockResolvedValueOnce({
    status: 401,
    json: () => Promise.resolve({}),
  });
  const consoleSpy = jest.spyOn(console, 'log').mockImplementation();

  render(<Login />);
  const loginButton = screen.getByRole('button', { name: /login/i });
  fireEvent.click(loginButton);

  await new Promise(process.nextTick);
  expect(consoleSpy).toHaveBeenCalledWith(new Error('Invalid credentials'));

  consoleSpy.mockRestore();
});

注意事项

  • 如果需要测试按钮禁用逻辑,可直接Mock validateForm 方法的返回值,比如返回false后验证按钮disabled属性为true
  • Jest v28及以上版本默认没有内置jsdom环境,需要手动安装jest-environment-jsdom,并在jest配置文件中添加testEnvironment: 'jsdom'配置
  • 若fetch返回值需要用到response.json()等其他方法,在mock返回值时补充对应方法即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 01:54:02