我正在尝试测试async / await函数,该函数使用axios进行api调用以获取用户。我是刚开始测试React应用程序并使用JEST(第一次尝试),因此无法运行测试。
我尝试在JEST中使用模拟功能。我的代码如下:
// component --users
export default class Users extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.getUsers = this.getUsers.bind(this);
}
/*does an api request to get the users*/
/*async await used to handle the asynchronous behaviour*/
async getUsers() {
// Promise is resolved and value is inside of the resp const.
try {
const resp = await axios.get(
"https://jsonplaceholder.typicode.com/users"
);
if (resp.status === 200) {
const users = resp.data;
/* mapped user data to just get id and username*/
const userdata = users.map(user => {
var userObj = {};
userObj["id"] = user.id;
userObj["username"] = user.username;
return userObj;
});
return userdata;
}
} catch (err) {
console.error(err);
}
}
componentDidMount() {
this.getUsers().then(users => this.setState({ users: users }));
}
/*****************************************************************/
//props(userid ,username) are passed so that api call is made for
//getting posts of s psrticular user .
/*****************************************************************/
render() {
if (!this.state.users) {
return (
<div className="usersWrapper">
<img className="loading" src="/loading.gif" alt="Loading.." />
</div>
);
}
return (
<div className="usersWrapper">
{this.state.users.map(user => (
<div key={user.id}>
<Posts username={user.username} userid={user.id} />
</div>
))}
</div>
);
}
}
//axios.js-mockaxios
export default {
get: jest.fn(() => Promise.resolve({ data: {} }))
};
//users.test.js
describe("Users", () => {
describe("componentDidMount", () => {
it("sets the state componentDidMount", async () => {
mockAxios.get.mockImplementationOnce(
() =>
Promise.resolve({
users: [
{
id: 1,
username: "Bret"
}
]
}) //promise
);
const renderedComponent = await shallow(<Users />);
await renderedComponent.update();
expect(renderedComponent.find("users").length).toEqual(1);
});
});
});
测试失败-
FAIL src / components / users.test.js(7.437s)●用户› componentDidMount›设置状态componentDidMount
期望(已接收).toEqual(预期)
期望值等于: 1个 收到: 0
请帮助我找出问题所在。我对测试reactapps完全陌生
答案 0 :(得分:0)
看起来像similar to this one。
问题是测试是否要比异步fetchUsers和setState更早完成(这也是异步操作)。要解决此问题,您可以通过done
回调进行测试,并将最后的期望值放入setTimeout(fn, 0)
-这样,将在所有异步操作完成后调用期望值:
it("sets the state componentDidMount", (done) => {
...
setTimeout(() => {
expect(renderedComponent.find("users").length).toEqual(1);
done();
}, 0);
});
正如评论中提到的那样,它是很棘手的修复程序,我希望这里是其他答案,并提供更多jest
的修复方法。
答案 1 :(得分:0)
据我了解,您正在尝试做的是等待对Promise的解析,该Promise被“隐藏”在componentDidMount()方法内部。
expect(renderedComponent.find("users").length).toEqual(1);
在这种情况下,在我看来不起作用,因为find()试图选择DOM元素。如果要检查状态,则需要使用state():
expect(renderedComponent.state("users").length).toEqual(1);
仍然,您将必须找到正确的方法来等待诺言的解决:
要参考之前的文章和评论,我看不到将async / await与任何一种酶方法(更新,实例或其他方法)结合使用不会产生任何效果。酶文档也没有朝着该方向提供任何提示,因为答应解决不是酶的工作。
前进的唯一健壮,干净且(jest-default)的方式是各种方法的混合。这是您的代码略有更改的地方:
// component --users
export default class Users extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.getUsers = this.getUsers.bind(this);
}
/*does an api request to get the users*/
/*async await used to handle the asynchronous behaviour*/
async getUsers() {
// Promise is resolved and value is inside of the resp const.
try {
const resp = await axios.get(
"https://jsonplaceholder.typicode.com/users"
);
if (resp.status === 200) {
const users = resp.data;
/* mapped user data to just get id and username*/
const userdata = users.map(user => {
var userObj = {};
userObj["id"] = user.id;
userObj["username"] = user.username;
return userObj;
});
return userdata;
}
} catch (err) {
console.error(err);
}
}
componentDidMount() {
// store the promise in a new field, not the state since setState will trigger a rerender
this.loadingPromise = this.getUsers().then(users => this.setState({users: users}));
}
/*****************************************************************/
//props(userid ,username) are passed so that api call is made for
//getting posts of s psrticular user .
/*****************************************************************/
render() {
if (!this.state.users) {
return (
<div className="usersWrapper">
<img className="loading" src="/loading.gif" alt="Loading.."/>
</div>
);
}
return (
<div className="usersWrapper">
{this.state.users.map(user => (
<div key={user.id}>
<Posts username={user.username} userid={user.id}/>
</div>
))}
</div>
);
}
}
//axios.js-mockaxios
export default {
get: jest.fn(() => Promise.resolve({data: {}}))
};
//users.test.js
describe("Users", () => {
describe("componentDidMount", () => {
it("sets the state componentDidMount",() => {
mockAxios.get.mockImplementationOnce(
() =>
Promise.resolve({
users: [
{
id: 1,
username: "Bret"
}
]
}) //promise
);
const renderedComponent = shallow(<Users/>);
//Jest will wait for the returned promise to resolve.
return renderedComponent.instance().loadingPromise.then(() => {
expect(renderedComponent.state("users").length).toEqual(1);
});
});
});
});
在做一些研究时,我遇到了这个post:老实说,我不知道这是否是一个好主意,但它在我的异步测试中有效,并且避免了对promise的其他引用在您的组件中。所以也可以尝试一下!