首页 > 解决方案 > 模拟由 Redux Provider 组件封装的 React 组件函数

问题描述

这是我的情况:我正在尝试对一个 React 组件 (TodoList) 进行单元测试,该组件在 Render 方法上只执行映射项目并显示它们。它使用 MapStateToProps 从 Redux 存储中获取项目(TodoItem)。

这是 TodoList 组件的 javascript 代码:

class TodoList extends React.Component {
    onRemoveClick = (id) => {
        diContainer.dataLayer.todo.remove(id);
    }

    render() {
        const todos = this.props.todos;
        if(!todos)
            return null;

        return (
            todos.map(todo => (
                <TodoItem key={todo.id} todo={todo} onRemove={this.onRemoveClick} />
            ))    
        );
    }
}

const mapStateToProps = (state) => ({
    todos: state.todos
});

export default connect(mapStateToProps)(TodoList);

我现在要测试的是,每当调用 TodoItem(子对象)内的按钮时,都会调用 onRemoveClick 委托方法。

我尝试将 Jest 提供的模拟功能与 Enzyme 结合使用。但是,因为 TodoList 从 Redux 获取他的数据,所以我必须用 Provider 组件包围我的 Enzyme mount() 调用并模拟存储。

这是我的测试代码:

import React from 'react';
import { mount } from 'enzyme';

import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';

import TodoList from '../../../../react/components/todo/TodoList';

describe('TodoList component', () => {
    //global arrange
    const storeState = {
       todos: [
                {
                    id: 'testId1',
                    title: 'testTitle1',
                    description: 'testDescription1'
                },
                {
                    id: 'testId2',
                    title: 'testTitle2',
                    description: 'testDescription2'
                },
        ]
    };

    const mockStore = configureStore();
    let store;

    beforeEach(() => {
        store = mockStore(storeState)
    });

    it('Removes a todo from the list if the remove button the todo was pressed', () => {
        //arrange
        //let mockFn = jest.fn();
        //TodoList.prototype.onRemoveClick = mockFn; => tried, doesn't work...

        const component = mount(
            <Provider store={store}>
                <TodoList />
            </Provider>
        );

        //component.instance() => tried working with this, but couldn't find it
        //component.instance().children() => is not the TodoList

        const items = component.find('.todoItem');

        //act
        const button = items.first().find('button');
        button.simulate('click');

        //assert
        //Todo: check function spy here
    });
});

我注释掉了一些我尝试过的东西。但是我似乎无法在我的测试代码中访问 TodoList 组件,因为 Provider 包装器......

有什么线索吗?

标签: javascriptreactjsreduxjestjsenzyme

解决方案


通过大量的试验和错误得到它修复。我能够通过酶查找功能访问 TodoList,这显然也适用于 ComponentNames(而不仅仅是普通的 HTML 选择器)。

第二个技巧是在 TodoList 组件上调用 forceUpdate() 并在父包装器上调用 update()。

it('Removes a todo from the list if the remove button on the todo was pressed', () => {
    //arrange
    const component = mount(
        <Provider store={store}>
            <TodoList />
        </Provider>
    );

    const list = component.find('TodoList').instance();
    let mockFn = jest.fn();
    list.onRemoveClick = mockFn;

    list.forceUpdate();
    component.update();

    //act
    const items = component.find('.todoItem');
    const button = items.first().find('button');
    button.simulate('click');

    //assert
    expect(mockFn).toHaveBeenCalled();
    expect(mockFn).toHaveBeenCalledTimes(1);
    expect(mockFn).toHaveBeenCalledWith('testId1');
});

推荐阅读