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

Redux-Form单元测试:无法触发表单onSubmit处理函数

解决Redux-Form单元测试中onSubmit处理函数未触发的问题

我来帮你排查这个问题——你遇到的是Redux-Form测试里常见的事件模拟和用法规范问题,咱们一步步来拆解和修复:

问题根源分析

你的测试里模拟提交后,submit函数完全没触发,主要有三个核心问题:

  1. 错误模拟了按钮事件:你在测试里给按钮调用simulate('submit'),但按钮本身并没有submit事件。点击type="submit"的按钮会触发父表单的submit事件,所以你应该模拟按钮的click事件,或者直接模拟表单的submit事件。

  2. 组件未遵循Redux-Form的标准用法:Redux-Form的核心是通过handleSubmit来封装表单数据收集、验证和默认行为阻止,但你的组件直接自己绑定了form的onSubmit,完全没用到props里的handleSubmit——这不仅浪费了Redux-Form的能力,也导致测试里的handleSubmit stub完全起不到作用。

  3. submit函数参数命名混淆:你的submit函数把事件对象命名为dataValues,虽然这不影响触发,但会误导后续逻辑(比如你调用dataValues.preventDefault()是对的,但变量名会让你误以为这是表单数据)。

分步修复方案

第一步:修正组件的表单提交逻辑,正确使用Redux-Form的handleSubmit

Redux-Form的handleSubmit会自动帮你处理:阻止默认提交行为、收集表单字段值、触发验证,然后把干净的表单数据传给你的提交函数。修改组件代码如下:

// 现在参数是真正的表单数据,不是事件对象
submit(formData) {
  console.log("called");
  // 这里不需要手动preventDefault,handleSubmit已经处理了
  // 可以在addBudget成功后调用reset
  return this.props.addBudget(formData)
    .then(() => this.props.reset());
}

render() {
  const { handleSubmit } = this.props;
  return (
    <div className='form-group has-danger'>
      {/* 用handleSubmit包裹你的submit方法,这是Redux-Form的标准写法 */}
      <form onSubmit={handleSubmit(this.submit.bind(this))}>
        <Field 
          name='budgetName' 
          component={this.categoryInput} 
          placeholder="Enter Category of Budget" 
          label="Budget Category"
        />
        <button type='submit' className='btn btn-primary'>Submit</button>
      </form>
    </div>
  )
}

第二步:修正测试代码,正确模拟提交并验证逻辑

你的测试需要确保触发正确的事件,并且处理异步逻辑(因为addBudget返回Promise)。修改后的测试代码:

import React from 'react';
import { AddBudgetForm } from '../../../src/Budget/components/AddBudgetForm';
import { SubmissionError } from 'redux-form';
import { shallow, configure } from 'enzyme';
import sinon from 'sinon';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });

describe("AddBudgetForm Component", () => {
  let budgetForm = null;
  let submitting, touched, error, reset, addBudget, addBudgetResponse, handleSubmit;

  beforeEach(() => {
    submitting = false;
    touched = false;
    error = null;
    reset = sinon.spy();
    // 让addBudget返回resolved的Promise,方便测试异步流程
    addBudgetResponse = Promise.resolve();
    // 模拟handleSubmit:接收组件的submit函数,返回一个可触发的函数
    handleSubmit = sinon.stub().returns((e) => {
      e.preventDefault();
      // 调用传入的submit函数,并传入模拟的表单数据
      handleSubmit.firstCall.args[0]({ budgetName: 'Test Grocery Budget' });
    });
  });

  const buildForm = () => {
    addBudget = sinon.stub().returns(addBudgetResponse);
    const props = {
      addBudget,
      submitting: submitting,
      fields: {
        budgetName: { value: '', touched: touched, error: error }
      },
      handleSubmit,
      reset
    };
    return shallow(<AddBudgetForm {...props}/>);
  };

  it ('Calls addBudget and reset after successful submission', async () => {
    budgetForm = buildForm();
    // 模拟按钮的click事件,这会触发表单的submit流程
    budgetForm.find('[type="submit"]').simulate('click', { preventDefault: sinon.spy() });
    
    // 等待异步的addBudget操作完成
    await addBudgetResponse;
    
    // 验证addBudget是否被正确调用
    expect(addBudget.calledOnceWith({ budgetName: 'Test Grocery Budget' })).toBe(true);
    // 验证reset是否在提交成功后被调用
    expect(reset.calledOnce).toBe(true);
  });
});

额外提示

  • 如果你的提交逻辑可能抛出SubmissionError(比如验证失败),可以在测试里模拟addBudget返回Promise.reject(new SubmissionError({...})),来测试错误分支。
  • 用async/await处理异步逻辑是Enzyme测试Redux-Form异步提交的标准方式,确保测试不会在Promise完成前就结束断言。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:09:57