我在父组件中有一个名为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.');
});
我收到此错误:
期望值等于:
“您的电子邮件已更新。”
已收到:
“”
答案 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.");
});
});