简体   繁体   English

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

[英]Return value of a mocked function does not have `then` property

I have the following async call in one of my React components: 我的React组件之一中有以下异步调用:

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

The goal here is to redirect the user to the index page only once the post has been persisted in Redux ( startAddPost is an async action generator that sends the data to an external API using axios and dispatches another action that will save the new post in Redux store; the whole thing is returned, so that I can chain a then call to it in the component itself). 目标是仅在帖子在Redux中持久化后才将用户重定向到索引页面( startAddPost是一个异步操作生成器,它使用axios将数据发送到外部API并调度另一个操作,该操作将新帖子保存在Redux中存储;将整个内容返回,以便我可以在组件本身中链接then调用它)。 It works in the app just fine, but I'm having trouble testing it. 它可以在应用程序中正常工作,但是我无法对其进行测试。

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('/');
});

So I obviously need this test to pass, but it fails with the following output: 因此,显然我需要此测试通过,但它失败,并显示以下输出:

● 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)

So how can I fix this? 那么我该如何解决呢? I suspect this is a problem with the test itself because everything works well in the actual app. 我怀疑这是测试本身的问题,因为在实际应用中一切正常。 Thank you! 谢谢!

Your code is not testable in the first place. 首先,您的代码不可测试。 You pass in a callback to the action and execute it after saving the data to the database like so, 您将回调传递给操作,然后将数据保存到数据库中后执行该回调,

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

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

The callback should be responsible for the above redirection in this case. 在这种情况下,回调应负责上述重定向。 The client code which uses the action should be like this. 使用该动作的客户端代码应该是这样的。

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

This makes your action much more flexible and reusable too. 这也使您的操作更加灵活和可重复使用。

Then when you test it, you can pass a stub to the action, and verify whether it is called once. 然后,当您对其进行测试时,可以将存根传递给该动作,并验证它是否被调用一次。 Writing a quality, testable code is an art though. 编写高质量,可测试的代码是一门艺术。

The problem with your code is that the startAddPost function is a mock function which does not return a Promise , but your actual this.props.startAddPost function does return a Promise . 您的代码的问题在于, startAddPost函数是一个模拟函数, 它不会返回Promise ,但是您实际的this.props.startAddPost函数确实会返回Promise

That's why your code works but fails when you try to test it, leading to the cannot read property.... error. 这就是为什么您的代码可以工作,但在尝试对其进行测试时失败,从而导致cannot read property....错误。

To fix this make your mocked function return a Promise like so - 要解决此问题,请让您的模拟函数返回Promise如下所示:

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

Read more about mockReturnValueOnce here . 在此处阅读有关mockReturnValueOnce更多信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM