测试通过子组件更新的父组件中的状态

时间:2019-01-12 20:00:48

标签: reactjs jestjs enzyme

我在父组件中有一个名为onFormSubmit的函数。我将此功能传递给子组件。在子组件中提交表单后,将调用子组件中的onFormSubmit函数以将值传递回父组件。然后,此onFormSubmit函数进行某种检查,并以此为基础,更新父组件中的状态。

我想模拟/存根这个ajax / api调用。我该如何实现?或者,如何以这种情况可以测试的方式编写代码。

“我的父母”组件如下所示:

class App extends React.Component {

state: { message: "" }; //I want to test the state of message

onFormSubmit = async (form) => {
    if (form.primaryEmail !== "") {
        const response = await axios.post("api/", form);
        this.setState(
            {
                message: (response.status === 200) ? "Your email has been updated." : ""
            });
    } else {
        this.setState({ message: "No Update" });
    }
}

render() {
    return (
        <div>
            <Child onSubmit={this.onFormSubmit} />
            <h4>{this.state.message}</h4>
        </div>
    );
}
}

我的孩子组件看起来像这样:

class Child extends React.Component {
state = {
    primaryEmail: "",
};

onPrimaryEmailChange = e => {
    this.setState({ primaryEmail: e.target.value });
}

onFormSubmit = e => {
    e.preventDefault();
    this.props.onSubmit(this.state); //passing the value back to parent component
}

render() {
    return (
        <form onSubmit={this.onFormSubmit}>
            <h3>Email Address</h3>
            <div>
                <input type="email" value={this.state.primaryEmail} onChange={this.onPrimaryEmailChange} />
            </div>
            <div>
                <button type="submit">Submit</button>
            </div>
        </form >
    );
}
}

我的测试如下:

test("When valid form is submitted, it should show a success message", () => {
const wrapper = mount(<App />);
wrapper.find("input").at(0).simulate("change", {
  target: {
    value: "a@b.c",
  }
});
wrapper.find('form').simulate('submit');
expect(wrapper.state('message')).toEqual('Your email has been updated.');
});

我收到此错误:

  

期望值等于:

     

“您的电子邮件已更新。”

     

已收到:

     

“”

1 个答案:

答案 0 :(得分:1)

碰巧的是,本周初我遇到了类似情况。这是我解决的方法。可能有更好的解决方案,但这在我的情况下有效。

  

免责声明:我正在将其从内存中直接写入StackOverflow答案字段,因此它可能不是100%准确。

首先,您应该模拟Axios,以便可以控制测试的API输出。实际上,您绝对不应该从测试用例执行HTTP请求,因为您不是在测试API,而是在测试组件如何响应特定的API响应。我的项目使用create-react-app,它将Jest配置为从项目根目录中的__mocks__文件夹中加载模拟。

__mocks__/axios.js

export default {
    // since you are only testing "axios.post()", I'm only mocking "post"
    post: jest.fn()
}

然后,在父组件的测试中,您可以为post函数指定一个模拟实现,该函数返回200响应(您正在测试的情况)。

__tests__/App.test.jsx

// in Jest context, this should load from __mocks__/axios.js
import axios from "axios";

test("When valid form is submitted, it should show a success message", () => {
    // Axios itself returns a Promise, so the mock should as well
    axios.post.mockImplementationOnce(
        (url, formData) => Promise.resolve({
            status: 200
        })
    );

    const wrapper = mount(<App />);

    // Optionally, test the default state to be an empty string
    expect(wrapper.state()).toHaveProperty("message");
    expect(wrapper.state().message).toBe("");

    wrapper.find("input").at(0).simulate("change", {
        target: {
            value: "a@b.c",
        }
    });

    wrapper.find("form").simulate("submit");

    // Optionally, test if Axios was called
    expect(axios.post).toHaveBeenCalled();

    // More optionally, test if it was called with the correct email address
    expect(axios.post).toHaveBeenCalledWith(
        expect.any(),
        expect.objectContaining({ primaryEmail: "a@b.c" })
    );

    // Note that even though Axios may have been called, the asynchronous
    // Promise may not have completed yet which means the state will not
    // have been updated yet. To be safe, let's use setImmediate(), which
    // should wait for any pending Promises to complete first
    setImmediate(async () => {
        // Now we can test that the state has changed
        expect(wrapper.state().message).toBe("Your email has been updated.");
    });
});