Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When should you use render and shallow in Enzyme / React tests?

People also ask

Should I use shallow or mount?

Always begin with shallow. If componentDidMount or componentDidUpdate should be tested, use mount. If you want to test component lifecycle and children behavior, use mount. If you want to test children rendering with less overhead than mount and you are not interested in lifecycle methods, use render.

What is shallow rendering in Enzyme?

Shallow rendering lets you render a component “one level deep” and assert facts about what its render method returns, without worrying about the behavior of child components, which are not instantiated or rendered. This does not require a DOM.

What is the ideal outcome of using shallow tests?

Shallow tests might be good quick win practice optimizing tests. Isolation. It's quite common to add a new dependency to a resuable directive or component. It tends to break other tests because something is missing in dependency injection tree.

Does React testing library use shallow rendering?

Here's the list of things that React Testing Library cannot do (out of the box): shallow rendering. Static rendering (like enzyme's render function).


As per the Enzyme docs:

mount(<Component />) for Full DOM rendering is ideal for use cases where you have components that may interact with DOM apis, or may require the full lifecycle in order to fully test the component (ie, componentDidMount etc.)

vs.

shallow(<Component />) for Shallow rendering is useful to constrain yourself to testing a component as a unit, and to ensure that your tests aren't indirectly asserting on behavior of child components.

vs.

render which is used to render react components to static HTML and analyze the resulting HTML structure.

You can still see the underlying "nodes" in a shallow render, so for example, you can do something like this (slightly contrived) example using AVA as the spec runner:

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));
});

Notice that rendering, setting props and finding selectors and even synthetic events are all supported by shallow rendering, so most times you can just use that.

But, you won't be able to get the full lifecycle of the component, so if you expect things to happen in componentDidMount, you should use mount(<Component />);

This test uses Sinon to spy on the component's 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();
});

The above will not pass with shallow rendering or render

render will provide you with the html only, so you can still do stuff like this:

test.only('render works', t => {

    // insert Test component here...

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

hope this helps!


The difference between shallow() and mount() is that  shallow() tests components in isolation from the child components they render while mount()goes deeper and tests a component's children.

For shallow() this means that if the parent component renders another component that fails to render, then a shallow() rendering on the parent will still pass.