【发布时间】:2017-05-22 01:59:42
【问题描述】:
我正在从组件的 componentDidMount 函数向我的 API http://localhost:3001/api/cards 发出 GET 请求,以便仅在第一次渲染组件后才发出 api 请求(如 react 官方指南所建议的那样)。
此 API 设置数组 data 的状态。在渲染函数中,我调用data.map 函数来渲染这个数组中的多个组件。我应该如何测试是否已经渲染了所需数量的组件?
我的组件:
//CardGrid.js
import React from 'react';
import { Card, Col, Row } from 'antd';
import 'antd/dist/antd.css';
import { parseJSON } from './commonfunction';
import './CardGrid.css';
export default class extends React.Component {
constructor()
{
super();
this.state = {
data: {},
};
}
fetchData = async () => {
try
{
const data = await parseJSON(await fetch('http://localhost:3001/api/cards'));
console.log('data');
console.log(data);
this.setState({ data });
}
catch (e)
{
console.log('error is: ');
console.log(e);
}
}
componentDidMount() {
this.fetchData();
}
render() {
return (
<div style={{ background: '#ECECEC', padding: '30px' }}>
<Row gutter={16}>
{Object.keys(this.state.data).map((title) => {
return (<Col span="6" key={title}>
<Card title={title} bodyStyle={{
'fontSize': '6em',
}}>{this.state.data[title]}</Card>
</Col>);
})}
</Row>
</div>
);
}
};
现在我想检查是否有与我的 API 指定的一样多的 Card 组件正在呈现。
我首先模拟 fetch 函数以返回 1 个元素。然后我使用全 DOM 渲染酶和mount 上述组件并期望它包含 1 个元素。
测试用例:
// It fails
import React from 'react';
import { Card } from 'antd';
import { mount } from 'enzyme';
import CardGrid from './CardGrid';
it('renders 1 Card element', () => {
fetch = jest.fn().mockImplementation(() =>
Promise.resolve(mockResponse(200, null, '{"id":"1234"}')));
const wrapper = mount(<CardGrid />);
expect(fetch).toBeCalled();
expect(wrapper.find(CardGrid).length).toEqual(1);
expect(wrapper.find(Card).length).toEqual(1);
});
除了找不到 Card 元素外,所有测试都通过了。甚至调用了 fetch 模拟函数。在我尝试查找 Card 组件之前,它会失败,直到我放置一个 setTimeout 函数。
//It succeeds
import React from 'react';
import { Card } from 'antd';
import { mount } from 'enzyme';
import sinon from 'sinon';
import CardGrid from './CardGrid';
it('renders 1 Card elements', async () => {
fetch = jest.fn().mockImplementation(() =>
Promise.resolve(mockResponse(200, null, '{"id":"1234"}')));
const wrapper = mount(<CardGrid />);
expect(fetch).toBeCalled();
expect(wrapper.find(CardGrid).length).toEqual(1);
await setTimeoutP();
expect(wrapper.find(Card).length).toEqual(1);
});
function setTimeoutP () {
return new Promise(function (resolve, reject) {
setTimeout(() => {
console.log('111111111');
resolve();
}, 2000);
});
}
有什么我不明白的概念吗?我应该如何理想地测试这种异步加载组件?我怎样才能更好地将它们设计成易于测试?任何帮助将不胜感激。谢谢
【问题讨论】:
-
你真的需要
await parseJSON吗?你没有包括那个来源,但我猜它不是异步的。 MDN 文档说“如果值不是承诺,它会将值转换为已解决的承诺,并等待它。”所以看起来它应该可以工作,但也许这种方法和测试框架会有所帮助? -
对不起,我没有包含 parseJSON 的来源。它基本上在 fetch API 调用返回的 Response 对象上调用
.json()。我需要等待它,因为它返回一个承诺。 developer.mozilla.org/en-US/docs/Web/API/Body/json -
在这种情况下,我认为解决方案是
awaitBOTH 承诺(fetch和parseJSON)。 -
使用
await (await result).json();,我得到TypeError: Already read。那是因为 body 已经被 .json() 读取过了,不能再读取了。 stackoverflow.com/questions/34786358/… -
你这里有两个 Promise,所以都需要解决。您正在模拟
fetch以返回已解决的承诺,因此这可能没问题(以及下面 Andreas 的等待),但您似乎也需要模拟parseJSON方法,例如parseJSONPromise = Promise.resolve({"id": "1234"});parseJSON = jest.fn().mockImplementation(() => parseJSONPromise);和await parseJSONPromise;和expect(parseJSON.).toBeCalled();
标签: javascript unit-testing reactjs jestjs enzyme