expect(...)。toHaveBeenCalled()即使调用了React组件方法也会失败

问题描述 投票:2回答:2

我试图测试是否使用酶和jest调用React组件方法。当<section>元素变得不聚焦(模糊)时,应该调用该函数。该组件连接到Redux存储,但它也按名称导出,以使Redux脱离等式。以下是该组件的简化版本:

export class Section extends React.Component {
  constructor(props) {
    super(props)
    this.formRef = React.createRef();
    this.submitForm = this.submitForm.bind(this);
    this.state = {
      formSubmitted: false
    }
  }

  submitForm() {
    console.log("form submitted");
    this.setState({ formSubmitted: true })
    if (this.formRef && this.formRef.current) {
      this.formRef.current.submit();
    }
  }

  render() {
    return (
      <section onBlur={this.submitForm}>
        <form ref={this.formRef} action={url} method='post'>
          <input type="text" name="something" />
        </form>
      </section>
    );
  }
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Section);

我已经为spyOn部分尝试了各种组合,因为它似乎是问题的根源。一切都失败了。

import React from 'react';
import { shallow } from 'enzyme';
import { Section } from './Section';

describe('Section', () => {
  it('should submit form on blur', () => {
    const wrapper = shallow(<Section content={{ body: [] }} />);
    const spy = spyOn(wrapper.instance(), 'submitForm');
    const spy2 = spyOn(Section.prototype, 'submitForm');
    const spy3 = jest.spyOn(wrapper.instance(), 'submitForm');
    const spy4 = jest.spyOn(Section.prototype, 'submitForm');

    wrapper.find('section').simulate('blur');
    expect(wrapper.state().formSubmitted).toEqual(true);
    expect(spy).toHaveBeenCalled();
    expect(spy2).toHaveBeenCalled();
    expect(spy3).toHaveBeenCalled();
    expect(spy4).toHaveBeenCalled();
  })
})

我给组件一个状态,并测试它除了测试expect(...).toHaveBeenCalled以验证函数是否实际被调用,似乎是这种情况。

console.log('form submitted')出现在控制台中。

测试expect(wrapper.state().formSubmitted).toEqual(true);通过,这表明正在调用正确的函数。但是,我不想仅仅为了测试而有一个不必要的状态。刚刚添加了一个状态断言正在调用“submitForm”方法。

断言expect(...).toHaveBeenCalled()都失败了,错误Expected spy to have been called, but it was not calledExpected mock function to have been called, but it was not called.

javascript reactjs redux jestjs enzyme
2个回答
3
投票

有趣的是,这个问题潜入了JavaScript的一些更不寻常的方面。


怎么了

submitForm最初定义为prototype method

class Section extends React.Component {
  ...
  submitForm() { ... }
  ...
}

...意思是你会像这样创建spy

jest.spyOn(Section.prototype, 'submitForm');

...然后它被重新定义为instance property中的constructor

this.submitForm = this.submitForm.bind(this);

...意思是你现在会像这样创建spy

jest.spyOn(wrapper.instance(), 'submitForm');

...但是仍然无效,因为onBlurthis.submitForm期间直接绑定到render

<section onBlur={this.submitForm}>

因此,当前编写代码的方式实际上使得无法监视submitForm因为创建spy需要instance但是instance在组件渲染之前不可用,并且onBlur在渲染期间直接绑定到this.submitForm


onBlur更改为调用this.submitForm的函数,以便每当onBlur触发时它将调用this.submitForm的当前值:

render() {
  return (
    <section onBlur={() => this.submitForm()}>
      <form ref={this.formRef} action={url} method='post'>
        <input type="text" name="something" />
      </form>
    </section>
  );
}

...然后当你用submitForm上的spy替换instance时,当spy射击时,onBlur会被调用:

describe('Section', () => {
  it('should submit form on blur', () => {
    const wrapper = shallow(<Section content={{ body: [] }} />);
    const spy = jest.spyOn(wrapper.instance(), 'submitForm');

    wrapper.find('section').simulate('blur');
    expect(wrapper.state().formSubmitted).toEqual(true);
    expect(spy).toHaveBeenCalled();  // Success!
  })
})

1
投票

实际上你想验证当submit()失去焦点时是否调用了形式的Section,对吧?因此,您无需验证是否已调用内部方法 - 它无法确保是否提交表单。在重构的情况下,它也会导致假阴性结果(内部方法被重命名,一切正常,但测试失败)。

你可以改为模拟ref对象。然后,您将能够验证表单是否已提交

it('should submit form on blur', () => {
  const wrapper = shallow(<Section content={{ body: [] }} />);
  wrapper.instance().formRef.current = { submit: jest.fn()};
  wrapper.find('section').simulate('blur');
  expect(wrapper.instance().formRef.current.submit).toHaveBeenCalled();
})

确定测试还依赖于组件的内部(包含ref的属性)。但是这种方式我认为测试更多......说可靠。

© www.soinside.com 2019 - 2024. All rights reserved.