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

Jest/Enzyme测试按钮点击失败:期望mock函数被调用

解决Jest/Enzyme测试中onAddClick方法相关断言失败问题

我最近卡在一个Jest/Enzyme测试上,其他点击事件的测试都能正常通过,但这个测试一直报错:

expect(jest.fn()).toHaveBeenCalled() expected mock function to have been called.

先贴出相关代码片段,方便排查问题:

渲染代码中的按钮点击事件

<button 
  className={!this.state.name || !this.state.label || this.state.valueStore === null ? `add-custom-field-button disabled` : `add-custom-field-button`} 
  id="test-addclick" 
  onClick={() => {this.onAddClick()}}
> 
  Create Field 
</button>

onAddClick方法实现

onAddClick = () => { 
  let obj = this.props.selectedFormJSON; 
  this.addValueAttribute().then(()=>{ 
    obj.FORM_COLUMN.push( { 
      Control: this.state.control, 
      CreateBy: this.props.user.userId, 
      Datatype: this.state.datatype, 
      Form_UID: this.props.selectedFormJSON.Form_UID, 
      Help: this.state.help, 
      ValueStore: this.state.valueStore 
    } ) 
    this.props.updateSelectedFormJSON(obj); 
    if(!this.props.isNewForm && this.state.valueStore) { 
      this.props.patchForm().then((res)=>{ 
        if(res.Forms[0] && (res.Forms[0].Code === '200' || res.Forms[0].Code===200)) { 
          toast(<div>Attribute Added Successfully!</div>, {type: toast.TYPE.SUCCESS,position: toast.POSITION.TOP_LEFT}) 
        } else { 
          toast(<div>Failed to Add Attribute!</div>, {type: toast.TYPE.ERROR,position: toast.POSITION.TOP_LEFT}) 
        } 
      }); 
    } else if(this.state.valueStore) { 
      this.props.postForm().then((res)=>{ 
        if(res.Forms[0] && (res.Forms[0].Code === '201' || res.Forms[0].Code===201)) { 
          toast(<div>Attribute Added Successfully!</div>, {type: toast.TYPE.SUCCESS,position: toast.POSITION.TOP_LEFT}) 
        } else { 
          toast(<div>Failed to Add Attribute!</div>, {type: toast.TYPE.ERROR,position: toast.POSITION.TOP_LEFT}) 
        } 
      }) 
    } 
    this.props.closeModal(); 
  }) 
}

addValueAttribute方法实现

addValueAttribute = () => { 
  return new Promise((resolve, reject) => { 
    if(this.state.valueStore) { 
      let {valueTables, valueDatatypes, service} = this.state; 
      let body = { 
        Name: this.state.name, 
        TableName: this.props.selectedFormJSON.Entity, 
        Datatype: this.state.datatype, 
        ChangeType: 'I' 
      } 
      fetch(service.URL+'/VALUE_ATTRIBUTE', { 
        headers: { 
          'Content-Type': 'application/json', 
          'Ocp-Apim-Subscription-Key': service.subscription_key, 
        }, 
        method: 'POST', 
        credentials: 'include', 
        body: JSON.stringify(body), 
      }) 
      .then((res) => { 
        res.status === 201 && resolve(); 
      }) 
      .catch(() => { 
        reject(); 
      }) 
    } else { 
      //Not a value attr 
      resolve() 
    } 
  }) 
}

测试代码

it("should call onAddClick", async () => { 
  // use an async test method 
  baseProps.closeModal.mockClear(); 
  baseProps.updateSelectedFormJSON.mockClear(); 
  const instance = wrapper.instance(); 
  const spy = jest.spyOn(instance, 'addValueAttribute'); 
  // spy on addValueAttribute... 
  spy.mockImplementation(() => Promise.resolve()) 
  // give any callbacks queued in PromiseJobs a chance to run 
  wrapper.find('#test-addclick').at(0).simulate('click'); 
  // simulate click 
  expect(baseProps.updateSelectedFormJSON).toHaveBeenCalled(); // SUCCESS 
  expect(baseProps.closeModal).toHaveBeenCalled(); // SUCCESS 
});

问题分析与修复方案

我仔细排查后发现,问题核心在于Promise异步执行的时机,以及按钮是否处于可点击状态这两个点:

1. 异步Promise的执行时机问题

虽然你标记了测试方法为async,但模拟点击后立刻做断言时,addValueAttribute返回的Promise回调还没执行完毕,导致依赖异步完成的方法调用还没触发。

修复方案:等待Promise回调执行完成

可以用await等待spy的Promise,或者等待事件循环清空:

it("should call onAddClick", async () => { 
  baseProps.closeModal.mockClear(); 
  baseProps.updateSelectedFormJSON.mockClear(); 
  const instance = wrapper.instance(); 
  const spy = jest.spyOn(instance, 'addValueAttribute'); 
  spy.mockImplementation(() => Promise.resolve()); 

  wrapper.find('#test-addclick').at(0).simulate('click'); 
  
  // 等待Promise回调执行完成
  await spy(); 
  // 或者用这行等待所有异步任务:await Promise.resolve();

  expect(baseProps.updateSelectedFormJSON).toHaveBeenCalled(); 
  expect(baseProps.closeModal).toHaveBeenCalled(); 
});

2. 按钮可能处于禁用状态

你的按钮有禁用逻辑:!this.state.name || !this.state.label || this.state.valueStore === null,如果测试时组件state不满足条件,按钮会被禁用,点击事件根本不会触发。

修复方案:确保测试时组件state满足可点击条件

在模拟点击前,设置正确的state:

// 在渲染wrapper后、模拟点击前设置state
wrapper.setState({
  name: 'test-name',
  label: 'test-label',
  valueStore: 'test-value-store' // 传入非null的值
});

wrapper.find('#test-addclick').at(0).simulate('click');

另外提个小优化:可以把按钮的onClick={() => {this.onAddClick()}}简化成onClick={this.onAddClick},减少不必要的函数创建。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:08:36