【问题标题】:Jest.fn() not working in React unit-testJest.fn() 在 React 单元测试中不起作用
【发布时间】:2017-05-06 22:22:53
【问题描述】:

我正在测试一个名为 handleSubmit 的组件方法(名称无关紧要......)。

测试

// ...imported all modules at the top, including enzyme

it('should submit form data', () => {
  let form = shallow(<Form />);
  let handleSubmit = jest.fn(); // <= this doesn't work!!

  form.find('.submit-btn').simulate('click');

  expect(form.find('.submit-btn').length).toEqual(1);
  expect(handleSubmit).toHaveBeenCalled();
});

组件

import React, { Component } from 'react';
import axios from 'axios';

class CarnetSidebarForm extends Component {
  constructor(props) {
    super(props);

    this.state = {
      title: ''
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(e) {
    const target = e.target;
    const value = target.value;
    const name = target.name;

    this.setState({
      [name]: value
    });
  }

  handleSubmit(e) {
    e.preventDefault();

    let payload = {
      title: this.state.title
    };

    this.postCard(payload);

    console.log('Payload: ', payload);
  }

  postCard(data) {
    return axios.post('http://localhost:4000/api/cards', data)
      .then(response => {
        console.log('Response: ', response.message);
      });
  }

  render() {
    return (
      <div className="card-form-panel">
        <form className="card-form" onSubmit={this.handleSubmit}>
          <div className="form-group">
            <label htmlFor="card-title-field">Title</label>
            <input className="form-control"
               type="text"
               placeholder="Title..."
               id="card-title-field"
               name="title"
               value={this.state.title}
               onChange={this.handleChange} />
          </div>

          <input className="card-submit-btn btn btn-primary" type="submit" value="Save" />
        </form>
      </div>
    );
  }
}

export default CarnetSidebarForm;

我不断收到此错误消息,现在很烦人:

expect(jest.fn()).toHaveBeenCalled()

Expected mock function to have been called.

但是如果我在测试中创建一个假组件,那么它就可以工作

it('should submit form data', () => {
  let handleSubmit = jest.fn();

  // create a fake component
  let form = mount(
    <form onSubmit={handleSubmit}>
      <input className="submit-btn" type="submit" value="Save" />
    </form>
  );

  form.find('.submit-btn').simulate('submit');

  expect(form.find('.submit-btn').length).toEqual(1);
  expect(handleSubmit).toHaveBeenCalled();
});

enzyme 中的shallow()mount 与导入组件有关吗?我花了很多天寻找答案,但我迷路了。

【问题讨论】:

  • 你不应该将handleSubmit 作为道具传递吗?您正在设置 handleSubmit 但未在您的组件中使用它!
  • @Shaoz 你能发布你的组件的最小代码吗?您是将handleSubmit 传递为prop 还是组件方法?
  • @HardikModha,感谢您的回复。 handleSubmit 是一个组件方法。我已经用组件代码更新了我的问题。
  • 感谢您的更新。您需要按照@rauliyohmc 的建议模拟组件方法。你试过了吗?
  • @HardikModha,是的,试试他的建议,但我仍然遇到同样的错误Expected mock function to have been called. 所以我认为shallowmountjest.fn() 有问题。

标签: javascript unit-testing reactjs jestjs


【解决方案1】:

添加到@rauliyohmc 答案。问题是,即使在模拟了组件方法之后,它也没有被调用,而是调用了实际的方法。所以,在花了一些时间之后,我找到了一个解决方案。在模拟其方法后,您需要 forceUpdate 您的组件。

it('should submit form data', () => {
  let form = mount(<Form />); // need to use mount to make it work.
  form.instance().handleSubmit = jest.fn();
  form.update(); // equivalent to calling form.instance().forceUpdate();

  form.find('.submit-btn').simulate('submit'); // simulated event must be submit

  expect(form.find('.submit-btn').length).toEqual(1);
  expect(form.instance().handleSubmit).toHaveBeenCalled();
}); 

最小示例:gist

【讨论】:

  • 它根本不工作。现在我收到此错误Method “props” is only meant to be run on a single node. 0 found instead.
  • 似乎错误来自另一个测试用例,因为在这个测试用例中您根本没有访问道具。你能把所有其他的测试用例注释掉看看吗?
  • 我注释掉了除了你的解决方案之外的所有东西,但它仍然给我错误Method “props” is only meant to be run on a single node. 0 found instead. 所以它必须来自这个测试用例。正如你所说,我们没有访问任何道具,那为什么要提到它?
  • 好的,我将选择器修改为form.find('.submit-btn').first(),错误Method “props” is only meant to be run on a single node. 0 found instead. 全部消失了。但现在我最喜欢的错误Expected mock function to have been called. 又回来了:(
  • 您需要安装组件。不知何故,浅层渲染不起作用。我也更新了我的答案。我还创建了一个要点here 作为最小示例。
【解决方案2】:

问题是你不是在模拟组件方法本身,而是创建一个新的模拟函数并将其分配给一个随机变量。

在渲染前尝试通过对象原型模拟方法如下:

jest.mock('../Form'); // mock the component before importing it using the right path
import Form from '../Form';
...
it('should submit form data', () => {
  Form.prototype.handleSubmit = jest.fn();
  let form = shallow(<Form />);

  form.find('.submit-btn').simulate('submit');

  expect(form.find('.submit-btn').length).toEqual(1);
  expect(Form.prototype.handleSubmit).toHaveBeenCalled();
}); 

注意:不确定您使用的是哪个 jest 版本,但自 Jest v15 起,默认情况下禁用自动模拟,因此您需要在将模块导入文件之前显式模拟模块。

【讨论】:

  • 感谢您的回复。但我仍然收到相同的Expected mock function to have been called. 错误。请您解释一下为什么当我在导入的组件上使用shallowmount 时,不会调用jest.fn()?因为我正在遵循教程和您的答案中的所有步骤,但仍然无法正常工作。但是,如果我在测试本身中mount 我自己的假组件,那么它就可以工作。
  • 检查我的问题,我已经用我在测试中创建的假组件更新了它。它有效。但不是进口的。
  • 您可以尝试通过原型来模拟该方法吗?我编辑了我的代码 sn-p 的前两行。不要忘记在导入之前模拟你的模块。另外,正如我在假组件中看到的那样,最好模拟“提交”事件而不是“点击”。
【解决方案3】:
<input className="card-submit-btn btn btn-primary" type="submit" value="Save" />

您的组件没有使用 className .submit-btn,它使用的是 .card-submit-btn

【讨论】:

    猜你喜欢
    • 2016-03-21
    • 1970-01-01
    • 2014-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-23
    • 2013-02-20
    相关资源
    最近更新 更多