React 模态对话框的内容不可用于使用 mount() 的酶测试

IT技术 reactjs unit-testing modal-dialog jestjs enzyme
2021-04-24 20:57:52

我有一个带有模态对话框的 React 组件(使用 构建reactstrap,但其他人报告了react-bootstrap与其他类型的模态组件类似的问题)。Enzyme 无法在模态中找到任何组件,即使它们在实际应用程序中呈现良好。最小的例子:

import React from 'react'
import { Modal } from 'reactstrap'

export default class MyModal extends React.Component {

    render() {
        return (
            <div className="outside"> Some elements outside of the dialog </div>
            <Modal isOpen={this.props.modalOpen}>
                <div className="inside"> Content of dialog </div>
            </Modal>
         );
    } 
}

我想像这样测试内容(在这种情况下使用jest

import React from 'react'
import MyModal  from './MyModal'
import { mount } from 'enzyme'

it('renders correctly', () => {
    const wrapper = mount( <MyModal modalOpen/> );

    expect(wrapper).toMatchSnapshot();

    // Passes
    expect(wrapper.find('.outside')).toHaveLength(1);

    // Fails, 0 length
    expect(wrapper.find('.inside')).toHaveLength(1);
});

测试正确地找到了 Modal 之外的内容,但没有找到里面的任何内容。查看快照表明,实际上,里面没有任何东西<Modal>被渲染。但是,如果我替换mountshallow. 问题是我需要mount测试生命周期方法,如componentDidMount.

为什么不mount呈现模态的内容?我认为重点是它渲染了整个子元素树。

3个回答

编辑:这在 React 16 + Enzyme 3 中不再是问题,因为React 16 支持门户组件

在 React 15 及之前,问题是模态对话框(在大多数实现中)是一个门户组件。这意味着它创建直接附加到文档根的 DOM 元素,而不是父 React 组件的子元素。

created byfind方法是从顶层组件创建的元素开始遍历DOM,所以找不到modal的内容。但是 Enzyme不附加到 DOM,而是构建自己的包含模态内容的组件树。ReactWrappermountshallow

要测试门户组件,您首先需要找到已附加到文档正文的 DOM 元素。然后你可以ReactWrapper在它们周围创建一个新的,以便所有常见的酶功能工作:

import React from 'react'
import MyModal  from './MyModal'
import { mount, ReactWrapper } from 'enzyme'

it('renders correctly', () => {
    const wrapper = mount( <MyModal modalOpen/> );

    expect(wrapper).toMatchSnapshot();

    // Passes
    expect(wrapper.find('.outside')).toHaveLength(1);

    // Construct new wrapper rooted at modal content
    inside_els = document.getElementsByClassName("inside")[0]
    inside_wrapper = new ReactWrapper(inside_els, true)

    // Passes
    expect(inside_wrapper.find('.inside')).toHaveLength(1);
});

目前,这是Enzyme 中的一个开放错误

更新:在测试完成后,Enzyme 似乎也将模态附加到 DOM,因此您最终可能会在以后的测试中打开多个对话框。如果这是一个问题,您可以在每次测试后清除 DOM,如下所示:

afterEach(() => {
  var node = global.document.body;
  while (node.firstChild) {
    node.removeChild(node.firstChild);
  }
}); 

尝试模拟负责显示模态的 createPortal ReactDOM.createPortal = jest.fn(modal => modal);

如果您使用的是旧版本的 Enzyme,您可以将容器元素传递到mount您想要Modal呈现的位置,如下所示:

import React from 'react'
import MyModal  from './MyModal'
import { mount } from 'enzyme'

describe(() => {
    let wrapper;
    beforeEach(() => {
        const container = document.createElement("div");
        document.body.appendChild(container);
        wrapper = mount( <MyModal modalOpen/> , {attachTo: container});
    });

    it('renders correctly', () => {
        expect(wrapper).toMatchSnapshot();

        // Passes
        expect(wrapper.find('.outside')).toHaveLength(1);

        // Passes now
        expect(wrapper.find('.inside')).toHaveLength(1);
    });

})