如何在 React 应用程序中使用 JEST 测试向 api 发出 axios 请求的异步函数
Posted
技术标签:
【中文标题】如何在 React 应用程序中使用 JEST 测试向 api 发出 axios 请求的异步函数【英文标题】:How to test an async function which does an axios request to an api using JEST in a React application 【发布时间】:2019-05-29 07:35:52 【问题描述】:我正在尝试测试一个 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" />
</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);
);
);
);
测试失败 -
失败 src/components/users.test.js (7.437s) ● 用户 › componentDidMount › 设置状态 componentDidMount
expect(received).toEqual(expected)
期望值等于: 1 已收到: 0
请帮我找出问题所在。我对测试 reactapps 完全陌生
【问题讨论】:
您问题中的代码没有显示getUsers
的调用方式,也没有显示您的测试断言。有没有您遗漏的componentDidMount
方法?还是您的测试直接致电getUsers
?如果删除 try
/catch
会发生什么?
getUsers() 在 componentDidMount() 中被调用。它在发布的代码中。
非常抱歉;我以某种方式错过了滚动条。
shallow
不是异步方法,在它之前不需要await
。看起来测试提前完成,然后组件在获得用户后重新渲染,您可以检查setTimeout( () => expect(renderedComponent.find("users").length).toEqual(1), 0)
(在测试的最后)吗?
您提供的解决方案似乎有效。通过测试。谢谢。
【参考方案1】:
据我了解,您正在尝试做的是等待承诺的解决,该承诺“隐藏”在您的 componentDidMount() 方法中。
expect(renderedComponent.find("users").length).toEqual(1);
在我看来在这种情况下将不起作用,因为find() 正在尝试选择 DOM 元素。如果要查看状态,需要使用state():
expect(renderedComponent.state("users").length).toEqual(1);
不过,你必须找到正确的方法来等待承诺的解决:
要参考以前的帖子和 cmets,我看不出将 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" />
</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:老实说,我不知道这是否是一个好主意,但它在我的异步测试中有效,它避免了在你的零件。所以也可以随意尝试一下!
【讨论】:
【参考方案2】:看起来像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);
);
正如评论中提到的,这是一个hacky修复,我希望这里有其他答案,有更多jest
修复它的方法。
【讨论】:
你不应该通过添加超时来解决异步单元测试问题 @MartinVich,是的,伙计,我也觉得这有点老套。那你是怎么解决的? 你需要告诉 jest 它必须等待 promise 解决。见jestjs.io/docs/en/tutorial-async#async-await @MartinVich,好的,但是 1)在原始问题中没有办法捕捉已解决的承诺,因为承诺被封装到componentDidMount
2)即使我们抓住了 1 中的时刻,怎么会你赶上setState
完成时刻了吗?它也是异步的......你能详细说明答案吗?我很好奇correctly
是怎么解决的
没错。我认为componentDidMount
测试应该模拟getUsers
以返回一些值,然后测试该值是否传递给setState
。然后在单独的测试中检查getUsers
是否返回模拟异步结果以上是关于如何在 React 应用程序中使用 JEST 测试向 api 发出 axios 请求的异步函数的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Jest 在 React Native 中测试警报
如何使用 create-react-app 在 Jest 测试中忽略生成的 Relay 查询文件?
如何在使用 react-testing-library 和 jest 完成的单元测试中启用 react-i18n 翻译文件?
如何使用 Jest 和/或 Enzyme 获取嵌套在 React 组件中的元素的属性?