Reputation: 3272
I have the following component, Layout
:
const Layout = ({ children, data, ...otherProps }) => (
<ErrorBoundary>
<App render={({ isSidebarOpen, scrollTop, toggleSidebar }) => (
<React.Fragment>
<Helmet
title={get(data, 'site.siteMetadata.title')}
meta={[
{ name: 'description', content: get(data, 'site.siteMetadata.description') },
{ name: 'pinterest', content: 'nopin' },
{ name: 'og:title', content: 'Daniel Spajic' },
{ name: 'og:description', content: get(data, 'site.siteMetadata.description') },
{ name: 'og:type', content: 'website' },
{ name: 'og:url', content: get(data, 'site.siteMetadata.siteUrl') },
{ name: 'og:image', content: ogImage },
{ name: 'og:locale', content: 'en_AU' },
]}
>
<link rel="shortcut icon" type="image/png" href={favicon} />
<link href="https://afeld.github.io/emoji-css/emoji.css" rel="stylesheet" />
</Helmet>
<div id={PAGE_CONTENT_CONTAINER_ID}>
<Sidebar isOpen={isSidebarOpen} toggle={toggleSidebar} />
<div id={PAGE_CONTENT_ID}>
{children({ scrollTop, toggleSidebar, ...otherProps })}
</div>
</div>
</React.Fragment>
)}
/>
</ErrorBoundary>
);
As shown it renders an App
with a render
prop. The isSidebarOpen
and scrollTop
arguments for the prop are both from App
's state. toggleSidebar
is one of App
's methods.
I want to test a few things:
Sidebar
sets its toggle
prop to toggleSidebar
, and isOpen
prop to isSidebarOpen
children
function is called with an object containing scrollTop
, toggleSidebar
, and otherProps
as its argumentThese involve retrieving App
's state and methods for comparison. I've tried accessing its state with Enzyme, but it's not possible because state()
can only be accessed on the root node:
ShallowWrapper::state() can only be called on the root
Therefore how can I access App
's state and methods so I can test these things?
Upvotes: 1
Views: 313
Reputation: 222493
ShallowWrapper::state() can only be called on the root
may not be a problem because tested values should be preferably hard-coded in unit tests. It's better to make a test unintentionally fail where it should pass than to make it unintentionally pass where it should fail, the former is much easier to debug and fix.
Though it may be beneficial to get component state, at least for assertions.
const layoutWrapper = mount(<Layout/>);
const appWrapper = layoutWrapper.find(App).dive();
expect(appWrapper.state('isSidebarOpen')).toBe(false);
expect(appWrapper.first(Sidebar).props('isOpen').toBe(false);
appWrapper.setState({ isSidebarOpen: true });
expect(appWrapper.state('isSidebarOpen')).toBe(true);
expect(appWrapper.first(Sidebar).props('isOpen').toBe(true);
There's a lot of moving parts in this component, this is also suggested by that it should be tested with mount
and not shallow
. it may be beneficial to provide fine-grained isolated tests, i.e. test render
prop separately:
const layoutWrapper = mount(<Layout/>);
const appWrapper = layoutWrapper.first(App);
const Child = appWrapper.prop('render');
const childWrapper = shallow(<Child isSidebarOpen={false} ... />);
expect(childWrapper.find(Sidebar).props('isOpen').toBe(false);
...
And how App
state interacts with render
prop should be tested in dedicated App
component test.
Upvotes: 1