Redux-Form单元测试:无法触发表单onSubmit处理函数
解决Redux-Form单元测试中onSubmit处理函数未触发的问题
我来帮你排查这个问题——你遇到的是Redux-Form测试里常见的事件模拟和用法规范问题,咱们一步步来拆解和修复:
问题根源分析
你的测试里模拟提交后,submit函数完全没触发,主要有三个核心问题:
错误模拟了按钮事件:你在测试里给按钮调用
simulate('submit'),但按钮本身并没有submit事件。点击type="submit"的按钮会触发父表单的submit事件,所以你应该模拟按钮的click事件,或者直接模拟表单的submit事件。组件未遵循Redux-Form的标准用法:Redux-Form的核心是通过
handleSubmit来封装表单数据收集、验证和默认行为阻止,但你的组件直接自己绑定了form的onSubmit,完全没用到props里的handleSubmit——这不仅浪费了Redux-Form的能力,也导致测试里的handleSubmitstub完全起不到作用。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
相关产品推荐
相关产品推荐

