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

React测试问题:Jest Mock函数已执行但调用次数验证失败

解决React Jest+Enzyme测试中Redux注入函数mock不生效的问题

我之前踩过完全一样的坑!你遇到的问题核心在于类属性箭头函数的绑定时机,再加上Redux Provider的包装,导致你的mock函数没有被正确关联到onDeleteClick里的调用。虽然console.log执行了,但deleteAccount调用的是组件初始化时绑定的原始函数,不是你后来mock的那个。下面给你几个针对性的解决办法:

1. 直接通过组件props覆盖Redux注入的函数

如果你的父组件是通过connect把deleteAccount注入到props中的,最直接的方式就是在挂载组件时,手动传入mock函数覆盖Redux的注入值。这样能确保onDeleteClick里调用的是你的mock:

import React from 'react';
import { mount } from 'enzyme';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';
import ParentComponent from './ParentComponent';

const mockStore = configureStore([]);
const mockDeleteAccount = jest.fn(); // 提前创建干净的mock函数

describe('ParentComponent', () => {
  let store;

  beforeEach(() => {
    // 每次测试重新初始化store,避免跨测试污染
    store = mockStore({
      // 填入组件需要的基础state,保证组件能正常渲染
      auth: { user: {} }
    });
  });

  it('triggers deleteAccount when child button is clicked', () => {
    const wrapper = mount(
      <Provider store={store}>
        {/* 直接传入mock函数覆盖Redux注入的props */}
        <ParentComponent deleteAccount={mockDeleteAccount} />
      </Provider>
    );

    // 模拟子组件按钮的点击事件(根据你的实际选择器调整)
    wrapper.find('ChildComponent button').simulate('click');

    // 验证mock函数被调用
    expect(mockDeleteAccount).toHaveBeenCalledTimes(1);
  });
});

2. Mock整个action creator模块

如果deleteAccount是一个单独的action creator(比如从../actions/auth导入),你可以直接mock这个模块,确保组件内调用的是mock版本:

import React from 'react';
import { mount } from 'enzyme';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';
import ParentComponent from './ParentComponent';
// 导入action模块并mock
import * as authActions from '../actions/auth';
jest.mock('../actions/auth');

const mockStore = configureStore([]);

describe('ParentComponent', () => {
  let store;

  beforeEach(() => {
    store = mockStore({ auth: { user: {} } });
    // 重置mock状态,避免跨测试污染
    authActions.deleteAccount.mockReset();
  });

  it('triggers deleteAccount when child button is clicked', () => {
    const wrapper = mount(
      <Provider store={store}>
        <ParentComponent />
      </Provider>
    );

    wrapper.find('ChildComponent button').simulate('click');

    // 验证action creator被调用
    expect(authActions.deleteAccount).toHaveBeenCalledTimes(1);
  });
});

3. 替换组件实例上的类属性箭头函数

如果deleteAccount是父组件内部定义的类属性箭头函数(不是从props注入的),你可以直接替换组件实例上的这个函数——因为类属性箭头函数是绑定到实例的,直接替换就能生效:

import React from 'react';
import { mount } from 'enzyme';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';
import ParentComponent from './ParentComponent';

const mockStore = configureStore([]);

describe('ParentComponent', () => {
  let store;

  beforeEach(() => {
    store = mockStore({ auth: { user: {} } });
  });

  it('triggers deleteAccount when child button is clicked', () => {
    const wrapper = mount(
      <Provider store={store}>
        <ParentComponent />
      </Provider>
    );
    // 创建mock函数并替换组件实例上的deleteAccount
    const mockDeleteAccount = jest.fn();
    wrapper.instance().deleteAccount = mockDeleteAccount;

    wrapper.find('ChildComponent button').simulate('click');

    expect(mockDeleteAccount).toHaveBeenCalledTimes(1);
  });
});

关键注意事项

  • 类属性箭头函数在组件实例创建时就绑定了上下文和函数引用,所以一定要确保mock函数在组件挂载前就准备好,不要在挂载后再修改然后调用forceUpdate——这不会改变箭头函数的引用。
  • 每次测试前要重置mock函数(用mockReset()或mockClear()),避免之前的测试调用影响当前结果。

内容的提问来源于stack exchange,提问作者Oisín Foley

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 09:01:34