模拟函数的返回值没有`then`属性

时间:2017-12-02 04:16:10

标签: reactjs asynchronous testing redux jestjs

我在我的一个React组件中有以下异步调用:

onSubmit = (data) => {
    this.props.startAddPost(data)
        .then(() => {
            this.props.history.push('/');
        });
};

此处的目标是仅在将帖子保留在Redux中后将用户重定向到索引页面(startAddPost是异步操作生成器,使用axios将数据发送到外部API并调度另一个操作将在Redux商店中保存新帖子;返回整个内容,以便我可以在组件本身链接then调用它。它在应用程序中工作得很好,但我在测试时遇到了麻烦。

import React from 'react';
import { shallow } from 'enzyme';

import { AddPost } from '../../components/AddPost';
import posts from '../fixtures/posts';

let startAddPost, history, wrapper;

beforeEach(() => {
    startAddPost = jest.fn();
    history      = { push: jest.fn() };
    wrapper      = shallow(<AddPost startAddPost={startAddPost} history={history} />);
});

test('handles the onSubmit call correctly', () => {
    wrapper.find('PostForm').prop('onSubmit')(posts[0]);

    expect(startAddPost).toHaveBeenLastCalledWith(posts[0]);
    expect(history.push).toHaveBeenLastCalledWith('/');
});

所以我显然需要通过这个测试,但它失败了,输出如下:

● handles the onSubmit call correctly

TypeError: Cannot read property 'then' of undefined

at AddPost._this.onSubmit (src/components/AddPost.js:9:37)
at Object.<anonymous> (src/tests/components/AddPost.test.js:25:46)
at process._tickCallback (internal/process/next_tick.js:109:7)

那我怎么解决这个问题呢?我怀疑这是测试本身的问题,因为一切都在实际的应用程序中运行良好。谢谢!

2 个答案:

答案 0 :(得分:0)

您的代码首先是不可测试的。您将回调传递给操作并在将数据保存到数据库之后执行它,如此,

export function createPost(values, callback) {
  const request = axios.post('http://localhost:8080/api/posts', values)
    .then(() => callback());

  return {
    type: CREATE_POST,
    payload: request
  };
}

在这种情况下,回调应该负责上述重定向。使用该操作的客户端代码应该是这样的。

  onSubmit(values) {
    this.props.createPost(values, () => {
      this.props.history.push('/');
    });
  }

这使您的行动更灵活,也可以重复使用。

然后,当您测试它时,您可以将存根传递给操作,并验证它是否被调用一次。编写高质量,可测试的代码是一门艺术。

答案 1 :(得分:0)

您的代码存在的问题是startAddPost函数是一个模拟函数,返回 Promise ,但您的实际this.props.startAddPost功能确实返回承诺

这就是为什么你的代码可以工作但是当你尝试测试它时失败,导致cannot read property....错误。

要解决此问题,请使您的模拟函数返回Promise,如此 -

beforeEach(() => {
    startAddPost = jest.fn().mockReturnValueOnce(Promise.resolve())
    ...
});

详细了解mockReturnValueOnce here