javascript - React/Jest - 模拟获取并等待 componentDidMount 重新渲染

标签 javascript reactjs testing enzyme jestjs

我正在玩 react 和 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 调用成功完成后立即

第 1 天我应该看不到任何待办事项,但第 2 天我应该看到我的待办事项。

这是 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 are being fetched...)为什么?不应该一样吗?

如果只有 promise 才是根本原因,我怎么能等到所有 promise 完成呢?

最佳答案

一种方法是等待队列中的最后一个事件(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();
    });

});

关于javascript - React/Jest - 模拟获取并等待 componentDidMount 重新渲染,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48962822/

相关文章:

go - 如何在测试中期待JSON

c# - 用于生成 C# 类初始化器的工具

javascript - html 移动菜单大小调整

javascript - 在多个 Controller 中看到的 promise 分辨率

reactjs - Jest 如何在 package.json 脚本中设置文件模式

node.js - server.js 中的内存泄漏 | react /表达/SSR

javascript - 尝试在 js 中附加路由参数时出现问题

javascript - 创建动态数量的 Web Worker Javascript

javascript - 似乎无法通过 bool 值设置我的组件的样式

testing - iOS 8 Beta - 内部测试人员必须是技术人员?