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

React Bootstrap Typeahead测试未触发API调用问题排查

React Bootstrap AsyncTypeahead测试:axios.get spy未触发的问题解决

问题场景

为React Bootstrap AsyncTypeahead编写测试时,期望输入值触发HTTP GET请求,但jest spy始终未被调用,相关代码及错误如下:

组件代码

import React, { useState } from 'react';
import { render } from 'react-dom';
import './style.css';
import { AsyncTypeahead } from 'react-bootstrap-typeahead';
import 'react-bootstrap-typeahead/css/Typeahead.css';
import axios from 'axios';

export const Description = () => {
  const [tyOptions, setOpts] = useState([]);

  const handleSearch = (id) => {
    axios
      .get(`https://jsonplaceholder.typicode.com/todos/${id}`)
      .then(({ options }) => { // 此处解构错误,接口返回数据在data字段而非options
        console.log(options);
        setOpts(options);
      });
  };

  return (
    <div>
      <AsyncTypeahead
        id="test"
        labelKey="title"
        minLength={1}
        onSearch={handleSearch}
        options={tyOptions}
        placeholder="Search todo by id."
        renderMenuItemChildren={(option) => (
          <>
            <span>Test</span>
          </>
        )}
      />
    </div>
  );
};

测试代码

describe("App Component Testing", () => {
    beforeEach(() => {
        jest.clearAllMocks();
    });

    it('should get typeahead data', async () => {
        let axios = jest.spyOn(axios, 'get').mockReturnValue(Promise.resolve(
            {
                data: [1, 2, 3, 4, 5]
            }
        ));

        let component;

        await act(async () => {
            component = render(<Description/>);
        });

        let inputEl = component.queryByPlaceholderText(/Search todo by id./);

        userEvent.click(inputEl);
        userEvent.type(inputEl, 'test val');

        expect(axios.get).toHaveBeenCalled();
    });
});

错误信息

expect(jest.fn()).toHaveBeenCalled();
Expected number of calls: => 1
Received number of calls: 0

问题原因及解决步骤

1. 修复axios spy的变量覆盖问题

测试代码中重新声明let axios,覆盖了原本导入的axios实例,导致spy没有作用到组件实际使用的axios上。

修改方式:
去掉变量声明,直接对导入的axios进行spy:

import axios from 'axios'; // 确保导入axios

// ...

it('should get typeahead data', async () => {
    const axiosGetSpy = jest.spyOn(axios, 'get').mockResolvedValue({
        data: [1, 2, 3, 4, 5]
    });

    // ...

    expect(axiosGetSpy).toHaveBeenCalled();
});

2. 处理异步操作的等待逻辑

userEvent.type是异步操作,且AsyncTypeahead的onSearch触发后axios请求也是异步的,需要用waitFor等待spy被调用,否则断言会在请求触发前执行。

修改后的测试代码片段:

it('should get typeahead data', async () => {
    const axiosGetSpy = jest.spyOn(axios, 'get').mockResolvedValue({
        data: [{ id: 1, title: 'Test Todo' }]
    });

    const { queryByPlaceholderText } = render(<Description/>);
    const inputEl = queryByPlaceholderText(/Search todo by id./);

    userEvent.click(inputEl);
    await userEvent.type(inputEl, '1'); // 输入单个字符即可触发minLength=1的搜索

    await waitFor(() => {
        expect(axiosGetSpy).toHaveBeenCalledWith('https://jsonplaceholder.typicode.com/todos/1');
    });
});

3. 修复组件中的响应数据解构错误

组件中then(({ options }) => { ... })是错误的,jsonplaceholder返回的响应数据在data字段中,而非options,这会导致setOpts(options)设置为undefined,修正后:

const handleSearch = (id) => {
    axios
      .get(`https://jsonplaceholder.typicode.com/todos/${id}`)
      .then(({ data }) => { // 改为解构data
        setOpts([data]); // 单条todo是对象,需包装成数组符合options格式
      });
};

4. 移除不必要的act包裹

react-testing-library的render方法已经自动处理了act逻辑,不需要手动用act包裹render调用。

完整修改后的测试代码

import { render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import axios from 'axios';
import { Description } from './Description';

describe("App Component Testing", () => {
    beforeEach(() => {
        jest.clearAllMocks();
    });

    it('should get typeahead data', async () => {
        const axiosGetSpy = jest.spyOn(axios, 'get').mockResolvedValue({
            data: { id: 1, title: 'Test Todo' }
        });

        const { queryByPlaceholderText } = render(<Description/>);
        const inputEl = queryByPlaceholderText(/Search todo by id./);

        userEvent.click(inputEl);
        await userEvent.type(inputEl, '1');

        await waitFor(() => {
            expect(axiosGetSpy).toHaveBeenCalledTimes(1);
            expect(axiosGetSpy).toHaveBeenCalledWith('https://jsonplaceholder.typicode.com/todos/1');
        });
    });
});

内容的提问来源于stack exchange,提问作者Oam Psy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 06:25:15