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

React组件在Jest测试中无法触发回调函数的问题求助

React Jest测试点击事件报错:TypeError: tree.props.handleClick is not a function

我正在用React开发简易天气Web应用,编写Jest测试时组件快照生成正常,但触发点击事件时出现类型错误:

TypeError: tree.props.handleClick is not a function

我参照Jest文档写的测试,以为这是触发点击的正确方式,是不是对点击函数的引用有误?刚接触测试编写,求React+Jest测试的相关指导!


React组件代码

import React from "react"
import { useEffect, useState } from 'react'
// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'

const CityEntry = ({ weatherDatum, deleteLocation }) => {
  const capitalizeFirstLetter = (string) => {
    return string.charAt(0).toUpperCase() + string.slice(1);
  }

  const handleClick= () => {
    deleteLocation(weatherDatum.id)
  }

  return (
    <div className="city">
      <p className="location flex-item">{capitalizeFirstLetter(weatherDatum.location)} </p>
      <p className="temp flex-item">{weatherDatum.temperature}  &#x2109;</p>
      <p className="feels-like flex-item">Feels like: {weatherDatum.feelsLike}</p>
      <p className="description flex-item">{capitalizeFirstLetter(weatherDatum.description)}</p>
      <p><img className="icon flex-item" src={`https://openweathermap.org/img/w/${weatherDatum.icon}.png`}></img></p>
      <button className="delete" onClick={handleClick}>Delete</button>
    </div>
  )
}

export default CityEntry;

Jest测试代码

import renderer from 'react-test-renderer';
import CityEntry from '../src/Components/CityEntry.js'

it('deletes a city entry when clicked', () => {
  const component = renderer.create(
    <CityEntry weatherDatum={{ id: '', lat: '', lon: '', location: '', temperature: '', feelsLike: '', description: '', icon: '' }} />
  );
  let tree = component.toJSON();
  expect(tree).toMatchSnapshot()

  renderer.act(() => {
    tree.props.handleClick()
  });

  tree = component.toJSON();
  expect(tree).toMatchSnapshot()
})

生成的Jest快照

exports[`deletes a city entry when clicked 1`] = `
<div
  className="city"
>
  <p
    className="location flex-item"
  >
     
  </p>
  <p
    className="temp flex-item"
  >
      ℉
  </p>
  <p
    className="feels-like flex-item"
  >
    Feels like: 
  </p>
  <p
    className="description flex-item"
  />
  <p>
    <img
      className="icon flex-item"
      src="https://openweathermap.org/img/w/.png"
    />
  </p>
  <button
    className="delete"
    onClick={[Function]}
  >
    Delete
  </button>
</div>
`;

问题原因及修复方案

1. 核心错误原因

  • tree是组件渲染后的JSON快照,仅包含DOM节点的属性和结构,不包含组件内部的handleClick方法,直接调用tree.props.handleClick()必然报错。
  • 测试中未给CityEntry传入必填的deleteLocation props,即便点击逻辑触发,也会因该prop未定义抛出新错误。

2. 修复后的测试代码

import renderer from 'react-test-renderer';
import CityEntry from '../src/Components/CityEntry.js'

it('deletes a city entry when clicked', () => {
  // 创建jest模拟函数,用于验证回调是否被触发
  const mockDeleteLocation = jest.fn();
  // 构造测试用的天气数据
  const testWeatherData = { 
    id: '123', 
    lat: '', 
    lon: '', 
    location: 'london', 
    temperature: '72', 
    feelsLike: '70', 
    description: 'sunny', 
    icon: '01d' 
  };

  const component = renderer.create(
    // 传入完整的props
    <CityEntry weatherDatum={testWeatherData} deleteLocation={mockDeleteLocation} />
  );
  let tree = component.toJSON();
  expect(tree).toMatchSnapshot()

  // 找到按钮元素,触发它的onClick事件
  renderer.act(() => {
    tree.children[5].props.onClick();
  });

  // 验证模拟函数是否被正确调用,且传入了预期的id参数
  expect(mockDeleteLocation).toHaveBeenCalledWith('123');
  
  // 重新生成快照(若组件删除后有UI变化则保留,否则可省略)
  tree = component.toJSON();
  expect(tree).toMatchSnapshot()
})

3. 新手测试实用建议

  • 优先用@testing-library/react:它的API更贴近真实用户行为(比如fireEvent.click、screen.getByText),比直接操作快照更符合测试逻辑。
  • 测试行为而非实现:不要纠结组件内部方法名称,重点验证用户操作后的结果(如回调是否触发、UI是否更新)。
  • 必传所有依赖props:组件依赖的props必须在测试中提供,避免未定义导致的错误。
  • 用模拟函数验证回调:对于父组件传入的回调,用jest.fn()创建模拟函数,测试调用次数、参数是否符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 04:30:55