在Enzyme / React测试中,何时应该使用render和shallow?

138

在发布这个问题之前,我尝试在sqa stackexchange搜索,但我没有找到有关shallow和render的帖子,所以我希望有人能在这里帮助我。

在测试React组件时,何时应该使用shallow和render?根据airbnb文档,我对两者的区别做了一些看法:

  1. 由于shallow将组件作为一个单元进行测试,因此应该用于“父”组件。 (例如表,包装器等)

  2. Render用于子组件。

我提出这个问题的原因是,我很难弄清楚在特定情况下应该使用哪一个(尽管文档说它们非常相似)

那么,在特定场景下,如何知道应该使用哪一个?


4
shallow() 和 mount() 的区别在于,shallow() 测试组件时与其呈现的子组件隔离开来,而mount() 则更深入地测试一个组件的子组件。对于 shallow() 来说,这意味着如果父组件呈现另一个无法呈现的组件,那么在父组件上进行 shallow() 呈现仍将通过。 - Shyam Kumar
最好坚持使用Mount测试,因为它更可靠,即使您重构了代码,它也会中断,而不是使用Shallow,它可能不会中断,并且会让您产生虚假的安全感。 - Michael Freidgeim
3个回答

205
根据酶文档所述:
对于需要与DOM api交互或需要完整生命周期以完全测试组件(即componentDidMount等)的组件,使用完整DOM渲染的mount(<Component />)是理想的选择。
相比之下,浅渲染的shallow(<Component />)用于将组件作为单元进行测试,并确保测试不会间接断言子组件的行为。 render用于将React组件呈现为静态HTML并分析生成的HTML结构。
在浅层渲染中仍然可以看到底层“节点”,因此例如,您可以使用AVA作为规范运行器来执行以下操作(稍微有些牵强):
let wrapper = shallow(<TagBox />);

const props = {
    toggleValue: sinon.spy()
};

test('it should render two top level nodes', t => {
    t.is(wrapper.children().length, 2);
});

test('it should safely set all props and still render two nodes', t => {
    wrapper.setProps({...props});
    t.is(wrapper.children().length, 2);
});

test('it should call toggleValue when an x class is clicked', t => {
    wrapper.setProps({...props});
    wrapper.find('.x').last().simulate('click');
    t.true(props.toggleValue.calledWith(3));
});

注意,渲染设置props查找选择器甚至合成事件都受浅层渲染的支持,因此大多数情况下您只需使用它即可。但是,您将无法获得组件的完整生命周期,因此如果您希望在componentDidMount中发生某些事情,则应使用mount(<Component />);此测试使用Sinon来监视组件的componentDidMount
test.only('mount calls componentDidMount', t => {

    class Test extends Component {
        constructor (props) {
            super(props);
        }
        componentDidMount() {
            console.log('componentDidMount!');
        }
        render () {
            return (
                <div />
            );
        }
    };

    const componentDidMount = sinon.spy(Test.prototype, 'componentDidMount');
    const wrapper = mount(<Test />);

    t.true(componentDidMount.calledOnce);

    componentDidMount.restore();
});

上述内容无法通过浅渲染render。使用render将仅提供HTML,因此您仍然可以执行以下操作:
test.only('render works', t => {

    // insert Test component here...

    const rendered = render(<Test />);
    const len = rendered.find('div').length;
    t.is(len, 1);
});

1
我仍然不完全明白为什么这三个动词会带来不同的方法。例如,可以在shallow中使用wrapper.getNode(),但在render中却不能。有没有任何解释/链接/文档/博客可以帮助我理解这个问题? - Paulquappe
@HenryZhu 从文档中可以清楚地看出,渲染(render)比浅层渲染(shallow)更复杂,因为它实际上试图模拟该特定组件节点的DOM树。 - AGE
15
酶从v2迁移到v3后,在Shallow渲染中默认启用了生命周期方法。 - Abhinav Singi
2
这里有关于差异的很好的额外解释:https://github.com/airbnb/enzyme/issues/465#issuecomment-227697726 和 https://github.com/airbnb/enzyme/issues/465#issuecomment-229116418 - Dmitry Gonchar

19

shallow()与mount()的区别在于,shallow()测试组件时会将其与渲染的子组件隔离开来,而mount()则会深入测试组件的子级。

对于shallow()而言,这意味着如果父组件渲染了另一个未能成功渲染的组件,那么在父组件上进行shallow()渲染仍将通过。


2
当我测试组件的 props 时,我应该使用 shallowmount 吗? - Menai Ala Eddine - Aladdin

0

来自为什么我从不使用浅渲染 by Kent C. Dodds

使用浅渲染,我可以重构我的组件实现,而我的测试会出错。使用浅渲染,我可以破坏我的应用程序,但我的测试会说一切都还正常。

你的测试越像你的软件的使用方式,它们给予你的信心就越多。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接