React / Jest - 模拟获取并等待componentDidMount重新呈现

时间:2018-02-24 12:22:32

标签: javascript reactjs testing enzyme jest

我正在玩弄反应和开玩笑而且我遇到了以下情况,我根本无法弄明白我应该怎么做。

Todo.js

import React from 'react';
import PropTypes from 'prop-types';
import TodoItem from './TodoItem';
import {fetchTodoItems} from '../helpers/todo';

class Todo extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            todos: [],
            error: false,
            loading: true
        };

        this.updateMockState = this.updateMockState.bind(this);
    }

    updateMockState() {
        this.setState({
            todos: [{ id: 8101, text: "Cook fresh food", status: "completed" }],
            loading: false
        });
    }

    componentDidMount() {
        // TODO: add error handling

        fetchTodoItems().then(response => {
            this.setState({
                todos: response.data,
                loading: false
            })
        }, error => {});
    }

    render() {
        let content;

        // TODO: add error handling

        if (this.state.loading) {
            return (
                <div>
                    <div>
                        <button id="update-data" onClick={this.updateMockState}>Update State</button>
                    </div>
                    <p>Todos are being fetched...</p>
                </div>
            );
        }

        return (
            content ||
            <div>
                <div>
                    <button id="update-data" onClick={this.updateMockState}>Update State</button>
                </div>
                <p><b>{this.state.todos.length}</b> todos fetched so far</p>
                {this.state.todos.map(
                    (todo) => <TodoItem key={todo.id} id={todo.id} status={todo.status} text={todo.text} />)}
            </div>
        );
    }
}

Todo.proptypes = {
    timeout: PropTypes.number
};

export default Todo;

Todo.test.js

import React from 'react';
import ReactDOM from 'react-dom';
import renderer from 'react-test-renderer';
import { mount, shallow, render } from 'enzyme';
import Todo from '../components/Todo';
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import toJson from 'enzyme-to-json';

// TODO: remove sinon from NPM packages

Enzyme.configure({ adapter: new Adapter() });

const todos = { data: [{
    "id": 4404,
    "status": "active",
    "text": "Shopping List"
}, {
    "id": 7162,
    "status": "completed",
    "text": "Time Registration"
}]};

describe('Todo', () => {
    it('calls mock API', async () => {
        fetch = jest.fn().mockReturnValue(Promise.resolve(todos));
        const spy = jest.spyOn(Todo.prototype, 'componentDidMount');

        var component = mount(<Todo timeout={2000} />);
        component.instance().componentDidMount();
        expect(spy).toHaveBeenCalled();

        expect(toJson(component)).toMatchSnapshot();
    });
});

正如您所见,Todo组件是一个简单的组件,在componentDidMount内部调用API,获取响应并显示。在等待api呼叫时,会显示一些信息......还有一个用于虚拟状态更新的按钮,但现在这并不重要。

fetchTodoItems(文件是todo.js)

export const fetchTodoItems = () => {
    return fetch("data/todo.json").then(res => res.json());
};

我的问题如下:

  • 我想对Todo组件进行快照测试,如下所示:
    1. 首先,正确渲染(在API调用之前)
    2. API调用成功完成后

一,我应该看到没有待办事项,但在第二天,我应该看到我的待办事项。

这是TodoItem

import React from 'react';
import PropTypes from 'prop-types';

const TodoItem = (props) => {
    let htmlClass = [];
    if (props.status === 'completed') {
        htmlClass.push("todo-completed");
    }

    return (
        <ul>
            <p className={htmlClass.join(" ")}>
                <small>[#{props.id}]</small> {props.text} <i>({props.status})</i>
            </p>
        </ul>
    );
}

TodoItem.proptypes = {
    id: PropTypes.number.required,
    text: PropTypes.string.required,
    status: PropTypes.string.required
};

export default TodoItem;

到目前为止,我已尝试过以下方法:

  • 使用expect进行纯快照测试(component.toJSON())。toMatchSnapshot() - 在API调用后不显示结果

  • jest.spyON ...调用该方法,但之后toMatchSnapshot仍显示第一个没有数据的快照

  • return promise()。then(...仍然没有结果

任何帮助?

修改

如果我从componentDidMount中删除API调用并仅使用setState,那么快照测试会显示todos项目(没有第一种情况,它应该说 todos正在被提取...... )为什么?不应该是一样的吗?

如果只有承诺是根本原因,我怎么能等待所有承诺完成?

1 个答案:

答案 0 :(得分:0)

一种方法是等待最后一个事件(promise),如果要解析队列中的任何事件,然后再进行组件实例更新?

const allOver = () => new Promise((resolve) => setImmediate(resolve));

describe('Todo', () => {
    it('calls mock API', async () => {
        fetch = jest.fn().mockReturnValue(Promise.resolve(todos));
        var component = mount(<Todo timeout={2000} />);
        await allOver();
        component.update();
        expect(toJson(component)).toMatchSnapshot();
    });

});