【问题标题】:Using withRouter and connect, how does one assert in enzyme that history.push was called?使用 withRouter 和 connect,如何在酶中断言 history.push 被调用?
【发布时间】:2020-10-10 04:23:34
【问题描述】:

我正在使用 withRouter 并在无状态 React 组件中连接。我在组件中有一个按钮,它调用 mapDispatchToProps 提供的 props 中的方法。

我正在尝试编写一个测试,断言按下按钮会调用 history.push()。

到目前为止,我还没有找到一种方法来做到这一点。有些地方说像这样传入一个模拟历史对象

const historyMock = { push: jest.fn() };
...
<MyComponent history={mockHistory}/>

会这样做,但这不起作用。我认为对 withRouter 的调用会覆盖它。我也尝试将它放在 Provider 和 Router 中。没有工作。

我也尝试过模拟 mapDispatchToProps,或者在 setHistory 上设置一个间谍。这一切似乎都是不可能的。

我还尝试监视 MyComponent 函数以将道具传递给它 - 也是不可能的。

wrapper.setProps() - 也不起作用。

我有这些文件。

my.component.container.jsx

import React from 'react';
import { Button } from '@material-ui/core';

const MyComponent = (props) => {
    const {setHistory, id} = props;
    return (
        <React.Fragment>
            <Button id="history_button" onClick={() => {
                setHistory(id)
            }}>View</Button>
        </React.Fragment>
    )
};

export default MyComponent;

my.component.js

import { connect } from 'react-redux';
import { withRouter } from 'react-router';
import MyComponentContainer from './my.component.container';

export const mapStateToProps = (state) => {
    const id = state?.data?.id || null;
    return {id};
};

export const mapDispatchToProps = (dispatch, ownProps) => {
    return {
        setHistory: (id) => {
            const {history} = ownProps;
            history.push(`/path/${id}`);
        },
    };
};

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(MyComponentContainer));

my.component.test.js

import React from 'react';
import { BrowserRouter as Router } from "react-router-dom";
import MyComponent from './my.component';
import { Provider } from 'react-redux'
import configureStore from 'redux-mock-store'
import { mount } from 'enzyme';

describe('MyComponent', () => {

    afterEach(function() {
        jest.clearAllMocks();
        jest.restoreAllMocks();
    });

    const getNode = (wrapper, search) => {
        return wrapper.find(search).hostNodes();
    };

    it('history buttons calls history.push()', () => {
        const initialState = {data: {id: 'id-1'}};
        const mockStore = configureStore();
        const store = mockStore(initialState);
        const wrapper = mount(<Provider store={store}><Router><MyComponent/></Router></Provider>);
        const button = getNode(wrapper, '#history_button');
        button.simulate('click');
        // to do - assert that history.push was called with '/path/id-1'
    });
});

我将把它作为我不太喜欢的解决方案扔掉。我正在寻找一般的历史对象。

expect(window.history.length).toEqual(1);
expect(window.location.href).toEqual('http://localhost/');

button.simulate('click');

expect(window.history.length).toEqual(2);
expect(window.location.href).toEqual('http://localhost/path/id-1');

【问题讨论】:

    标签: javascript reactjs react-redux react-router enzyme


    【解决方案1】:

    我尝试像您一样将历史记录传递给Router 组件:

    <Router history={historyMock}>
    

    当我运行测试时,它说:

      console.warn node_modules/tiny-warning/dist/tiny-warning.cjs.js:13
        Warning: <BrowserRouter> ignores the history prop. To use a custom history, use `import { Router }` instead of `import { BrowserRouter as Router }`.
    

    一旦我按照建议更改了导入,传递 Router 模拟历史对象就起作用了。

    my.component.test.js

    import React from 'react';
    // import { BrowserRouter as Router } from "react-router-dom"; // Warning: <BrowserRouter> ignores the history prop. To use a custom history, use `import { Router }` instead of `import { BrowserRouter as Router }`.
    import { Router } from "react-router-dom";
    import MyComponent from './my.component';
    import { Provider } from 'react-redux'
    import configureStore from 'redux-mock-store'
    import { mount } from 'enzyme';
    
    describe('MyComponent', () => {
    
        afterEach(function() {
            jest.clearAllMocks();
            jest.restoreAllMocks();
        });
    
        const getNode = (wrapper, search) => {
            return wrapper.find(search).hostNodes();
        };
    
        it('history buttons calls history.push()', () => {
            const initialState = {data: {id: 'id-1'}};
            const mockStore = configureStore();
            const store = mockStore(initialState);
            const historyMock = {
                listen: () => {},
                location: {
                    pathname: 'fake-path-name',
                },
                push: jest.fn(),
            };
            const wrapper = mount(<Provider store={store}><Router history={historyMock}><MyComponent history={'this is ignored and useless'}/></Router></Provider>);
            const button = getNode(wrapper, '#history_button');
    
            button.simulate('click');
    
            expect(historyMock.push).toHaveBeenCalledTimes(1);
            expect(historyMock.push).toHaveBeenCalledWith('/path/id-1');
        });
    });
    

    【讨论】:

    • 我没有遇到同样的问题,但是使用纯 Router 而不是 MemoryRouterBrowserRouter 的注释对我来说是关键。谢谢!
    猜你喜欢
    • 2018-03-11
    • 2019-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-16
    • 1970-01-01
    • 2021-08-25
    相关资源
    最近更新 更多