Javascript 如何在componentDidMount中测试由异步调用创建的组件?

Javascript 如何在componentDidMount中测试由异步调用创建的组件?,javascript,unit-testing,reactjs,jestjs,enzyme,Javascript,Unit Testing,Reactjs,Jestjs,Enzyme,我正在向我的API发出GET请求http://localhost:3001/api/cards来自组件的componentDidMount函数,因此只有在第一次呈现组件后才发出api请求(如react官方指南所建议) 此API设置数组数据的状态。在render函数中,我调用data.map函数来渲染该数组中的多个组件。我应该如何测试是否渲染了所需数量的组件 我的组成部分: //CardGrid.js import React from 'react'; import { Card, Col,

我正在向我的API发出GET请求
http://localhost:3001/api/cards
来自组件的componentDidMount函数,因此只有在第一次呈现组件后才发出api请求(如react官方指南所建议)

此API设置数组
数据的状态。在render函数中,我调用
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>
        );
    }
};
除了找不到卡元素外,所有测试都通过了。甚至调用了fetchmock函数。在我尝试查找卡组件之前,它一直失败,直到我放置了一个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);
    });
}
//成功了
从“React”导入React;
从“antd”导入{Card};
从“酶”导入{mount};
从“sinon”进口sinon;
从“/CardGrid”导入CardGrid;
它('呈现1个卡片元素',异步()=>{
fetch=jest.fn().mockImplementation(()=>
解析(mockResponse(200,null,“{”id:“1234”}”);
const wrapper=mount();
expect(fetch).toBeCalled();
expect(wrapper.find(CardGrid.length).toEqual(1);
等待setTimeoutP();
expect(包装器.查找(卡片).长度).toEqual(1);
});
函数setTimeoutP(){
返回新承诺(功能(解决、拒绝){
设置超时(()=>{
console.log('111111');
解决();
}, 2000);
});
}
有什么我不理解的概念吗?理想情况下,我应该如何测试这种异步加载组件?如何更好地将它们设计为易于测试?任何帮助都将不胜感激。感谢您对获取结果的承诺以及
parseJSON
的承诺。因此,我们需要模拟
parseJSON
,并让它返回已解析的承诺。请注意,路径需要与测试文件相对

import {parseJSON} from './commonfunction'

jest.mock('./commonfunction', () => {parseJSON: jest.fn()}) //this will replace parseJSON in the module by a spy were we can later on return a resolved promise with


it('renders 1 Card elements', async () => {
    const result = Promise.resolve(mockResponse(200, null, '{"id":"1234"}')) 
    parsedResult = Promise.resolve({"id":"1234"})
    parseJSON.mockImplementation(()=>parsedResult)
    fetch = jest.fn(() => result)
    const wrapper = mount(<CardGrid />);
    await result;
    await parsedResult;

    expect(fetch).toBeCalled();
    expect(wrapper.find(CardGrid).length).toEqual(1);
    expect(wrapper.find(Card).length).toEqual(1);
});
从“/commonfunction”导入{parseJSON}
jest.mock('./commonfunction',()=>{parseJSON:jest.fn()})//这将用一个间谍替换模块中的parseJSON,如果我们稍后返回一个已解析的承诺
它('呈现1个卡片元素',异步()=>{
const result=Promise.resolve(mockResponse(200,null,“{”id:“1234”}”))
parsedResult=Promise.resolve({“id”:“1234”})
mockImplementation(()=>parsedResult)
fetch=jest.fn(()=>result)
const wrapper=mount();
等待结果;
等待解析结果;
expect(fetch).toBeCalled();
expect(wrapper.find(CardGrid.length).toEqual(1);
expect(包装器.查找(卡片).长度).toEqual(1);
});

如果没有setTimeoutP,它就无法工作。我甚至更新了()最后一行中的包装器我想如果你也添加一个模拟的
parseJSON
,那么你的答案就行了。从OP:
await-parseJSON(await-fetch(…
argh,你是对的,这会让测试很难进行,会更新答案你真的需要
await-parseJSON
?你没有包括这个源代码,但我猜它不是异步的。MDN文档说“如果该值不是承诺,它会将该值转换为已解析的承诺,并等待它。”因此,它似乎应该可以工作,但可能与此方法和测试框架有关?抱歉,我没有包含parseJSON的源代码。它基本上调用
.json()
在fetch API调用返回的响应对象上。我需要等待它,因为它返回一个承诺。在这种情况下,我认为解决方案是
wait
两个承诺(对于
fetch
parseJSON
)。使用
wait(wait result).json()
,我得到
类型错误:已读
。这是因为正文已使用.json()读取,无法再次读取。您在这里有两个承诺,因此都需要解决。您正在模拟
获取
以返回已解决的承诺,这样可能就可以了(以及下面Andreas的等待),但似乎您还需要模拟
parseJSON
方法,例如
parseJSONPromise=Promise.resolve({id:“1234”});
parseJSON=jest.fn().mock实现(()=>parseJSONPromise);
等待parseJSONPromise;
expect(parseJSON.).toBeCalled();
import {parseJSON} from './commonfunction'

jest.mock('./commonfunction', () => {parseJSON: jest.fn()}) //this will replace parseJSON in the module by a spy were we can later on return a resolved promise with


it('renders 1 Card elements', async () => {
    const result = Promise.resolve(mockResponse(200, null, '{"id":"1234"}')) 
    parsedResult = Promise.resolve({"id":"1234"})
    parseJSON.mockImplementation(()=>parsedResult)
    fetch = jest.fn(() => result)
    const wrapper = mount(<CardGrid />);
    await result;
    await parsedResult;

    expect(fetch).toBeCalled();
    expect(wrapper.find(CardGrid).length).toEqual(1);
    expect(wrapper.find(Card).length).toEqual(1);
});