如何对获取完成后呈现的 React 组件进行单元测试?

Ant*_*des 8 reactjs jestjs fetch-api jest-fetch-mock

我是一个 Jest/React 初学者。在玩笑中,it我需要等到所有承诺都已执行后再实际检查。

我的代码类似于:

export class MyComponent extends Component {
    constructor(props) {
        super(props);
        this.state = { /* Some state */ };
    }

    componentDidMount() {
        fetch(some_url)
            .then(response => response.json())
            .then(json => this.setState(some_state);
    }

    render() {
        // Do some rendering based on the state
    }
}
Run Code Online (Sandbox Code Playgroud)

当组件被挂载时,render()运行两次:一次在构造函数运行之后,一次在fetch()(in componentDidMount()) 完成并且链式承诺完成执行之后)。

我的测试代码类似于:

describe('MyComponent', () => {

    fetchMock.get('*', some_response);

    it('renders something', () => {
        let wrapper = mount(<MyComponent />);
        expect(wrapper.find(...)).to.have.something();
    };
}
Run Code Online (Sandbox Code Playgroud)

无论我从什么返回it,它都会在第一次render()执行之后但在第二次执行之前运行。例如,如果我 return fetchMock.flush().then(() => expect(...)),则返回的承诺在第二次调用之前执行render()(我相信我能理解为什么)。

我怎样才能等到第二次render()在运行之前被调用expect()?

Ant*_*des 0

我找到了一种方法来完成我最初要求的事情。我(还)不知道这是否是一个好的策略(事实上我必须在之后立即重构该组件,所以这个问题不再与我正在做的事情相关)。无论如何,这是测试代码(解释如下):

import React from 'react';
import { mount } from 'enzyme';
import { MyComponent } from 'wherever';
import fetchMock from 'fetch-mock';

let _resolveHoldingPromise = false;

class WrappedMyComponent extends MyComponent {

    render() {
        const result = super.render();
        _resolveHoldingPromise && _resolveHoldingPromise();
        _resolveHoldingPromise = false;
        return result;
    }

    static waitUntilRender() {
        // Create a promise that can be manually resolved
        let _holdingPromise = new Promise(resolve =>
            _resolveHoldingPromise = resolve);

        // Return a promise that will resolve when the component renders
        return Promise.all([_holdingPromise]);
    }
}

describe('MyComponent', () => {

    fetchMock.get('*', 'some_response');

    const onError = () => { throw 'Internal test error'; };

    it('renders MyComponent appropriately', done => {
        let component = <WrappedMyComponent />;
        let wrapper = mount(component);
        WrappedMyComponent.waitUntilRender().then(
            () => {
                expect(wrapper.find('whatever')).toBe('whatever');
                done();
            },
            onError);
    });
});
Run Code Online (Sandbox Code Playgroud)

主要思想是,在测试代码中,我对组件进行子类化(如果这是 Python,我可能会对它进行猴子修补,在本例中其工作方式或多或少相同),以便它的render()方法发送一个信号,表明它被执行。发送信号的方法是手动解析 Promise。创建 Promise 时,它​​会创建两个函数:resolve 和reject,这两个函数在调用时会终止 Promise。让 Promise 外部的代码解析 Promise 的方法是让 Promise 在外部变量中存储对其解析函数的引用。

感谢 fetch-mock 作者 Rhys Evans,他好心地向我解释了手动解析承诺技巧。