How to test nested React components? - reactjs

I have the following situation where I have the following functionality I'd like to test, however, I can't seem to locate in the debug statements nor Enzyme's docs on how to achieve this:
wrapper = mount(
<SampleProvider data={data}>
<App />
</SampleProvider>
);
and I have App wrapped in a higher order function as follows:
#applyData
class App extends Component {
render() {
return <div>App</div>
}
}
I'd like to first, test whether applyData is correctly passing data in as props and the ability to check other conditions, etc. In short, the provider sets data inside of context and applyData basically passes the data as props into the wrapped component.
I've tried mount, shallow, etc... however, I can't get the information I need with any of the following:
wrapper.find(App)
wrapper.children()
wrapper.instance()

Related

Testing React Components that get state + setState properties passed by parent

How would you test React components that accept state hooks as properties?
For example
In the app, this ChildComponent is rendered like so:
<App>
<OtherComponent />
<div>
<ChildComponent selectedProperty={selectedProperty}
setSelectedProperty={setSelectedProperty} />
</div>
</App>
In my test, i'm trying to do this:
component = await render(
<ChildComponent selectedProperty={selectedProperty}
setSelectedProperty={setSelectedProperty} />
);
However I don't know how I would pass in these two properties selectedProperty and setSelectedProperty which in the app is handled by the parent component.
Thank you.
As a minimum example, fulfilling only the requirements you specified, this should work:
const selectedProperty = 'whatever';
const setSelectedProperty = () => {};
component = await render(
<ChildComponent selectedProperty={selectedProperty}
setSelectedProperty={setSelectedProperty} />
);
In case you need to verify that ChildComponent actually called setSelectedProperty, you can use a jest mock.
const selectedProperty = 'whatever';
const setSelectedProperty = jest.fn();
component = await render(
<ChildComponent selectedProperty={selectedProperty}
setSelectedProperty={setSelectedProperty} />
);
expect(setSelectedProperty).toHaveBeenCalled();
And of course, the whole of Jest function mocks are available to you (documented here: https://jestjs.io/docs/mock-functions).
------ Edit -------
I think you're actually asking: "How can I change state passed to a component in response to that component calling a set...?". Excellent question! You can either not (see more below) or check answers to this question: How to test a prop update on React component
The reason I would suggest to not to is because 1. your test is growing too much and 2. it's not necessary. Presumably you want to test two things:
When something happens, setSelectedProperty is called, and
The component renders properly for different values of selectedProperty.
There is no need to test those together; test them separately. If your component holds so much state that you feel you need to test them together, it may be that your component is too stateful. React should be as "functional" as possible and without side effects. If your component is becoming unmanageable and difficult to test in this regard, don't give up testing, redesign the component.

How to test that a Redux dispatch function gets called during componentDidMount in React class component?

Here is my basic React setup:
I have a React class Component
That is connected to my Redux store using connect()
That fires a function during componentDidMount
and that function is one that the Component has access to via mapDispatchToProps (implicitly)
I want to test that the above more or less works, that a specific function, in this cases fetchComments gets called during the componentDidMount lifecycle even of my component.
Here is the relevant component code:
class CommentContainer extends React.Component{
componentDidMount(){
this.props.fetchComments(this.props.resourceId)
}
...
export default connect(mapStateToProps, { authorizeUser, addComment, fetchComments })(CommentContainer)
For testing, I have tried A LOT of different approaches. The only thing I can get to work is not to test that fetchComments has been called (which from my understanding would be the best way to isolate functionality), but to test that the store gets updated in a specific way (which would only happen if fetchComments gets called).
The test I have that works:
import { render } from '#testing-library/react'
...
it ('loads comments upon rendering', ()=> {
const store = createStore(rootReducer, applyMiddleware(thunk)) //I've also tried mockStores, but that didn't seem to work well with the connect() method in the component
expect(store.getState().comments.loadStatus).toBe(null)
render(<Provider store={store}>
<Router>
<CommentContainer
relativePath={"/resources/:id"}
resourceId={1}
/>
</Router>
</Provider>
)
expect(store.getState().comments.loadStatus).not.toBe(null)
}
What I would like to do is something more along the lines of:
const spy = jest.spyOn(store, "dispatch")
render(<Provider store={store}>
<Router>
<CommentContainer
relativePath={"/resources/:id"}
resourceId={1}
/>
</Router>
</Provider>
)
expect(spy).toHaveBeenCalledWith(fetchComments)
However, when I do this the test fails. The spy gets called twice, and the output I get is:
Expected: [Function fetchComments]
Received
1: [Function anonymous]
2: [Function anonymous]
I gather this is happening because under the hood, store.dispatch is getting passed an anonymous function with the "work" of fetchComments but not getting passed the actual function fetchComments. Is that accurate?
I have also tried, among other things, creating a mock function of fetchComments and passing that as a prop directly to CommentContainer, but that prop seems to get overwritten by the connect() method and mapDispatchToProps
Another thing I tried was to use Enzyme and shallow and mount instead of react-testing-library's render and then use the .props() method to retrieve fetchComments from CommentContainer, but when I did that I got an error that prop was "Read Only" so I couldn't do something like jest.spyOn(wrapper.props, 'fetchComments')
I'd appreciate any feedback on how to make this work or how I should test this functionality.

Handling Apollo-wrapped descendants in unit tests that mount the React component

I've been working with ApolloJS in React (via react-apollo) for the past several months and have learned a number of the tricks and challenges associated with unit testing Apollo-wrapped components.
When testing a component that is directly wrapped with Apollo, I export and test the component before it is wrapped with HoC returned by graphql. When testing a component that has an Apollo-wrapped component as a descendant, I use Enzyme's shallow rendering whenever possible to prevent that descendant from mounting. If full-DOM rendering via mount is required, I use a MockedProvider from Apollo's test utils so that the descendants don't throw an error trying to access this.context.
I have not found a solution, however, for the following case: a component with Apollo-wrapped descendants needs to be tested using full-DOM rendering but I also need to make assertions involving the component instance (e.g. state, instance methods, etc). To avoid issues with descendants, I have to wrap the component in a mocked provider, but that means than any assertions on the wrapper are operating on the MockedProvider instance and not the component I want to be testing.
An example:
import { mount } from 'enzyme'
import { MockedProvider } from 'react-apollo/lib/test-utils'
// This component has descendants that are wrapped in Apollo and
// thus need access to `this.context` provided by an Apollo provider
import Assignments from 'app/components/assignments
...
describe('<Assignments />', function() {
it('sets sorted assignments in initial state', function() {
const assignments = [...]
const wrapper = mount(
<MockedProvider>
<Assignments assignments={assignments} />
</MockedProvider>
)
// This will fail because the wrapper is of the MockedProvider
// instance, not the Assignments instance
expect(wrapper.state('assignments')).to.eql([...])
})
})
I've tried to find a way via Enzyme to access the component instance of a child rather than the root, which as far as I can tell is not supported. I've also been trying to find alternatives to needing the MockedProvider in these tests but have not found anything yet.
Has anyone figured out a workaround for this sort of situation, or is there a different approach I should be taking to deal with nested Apollo-wrapped components?
I've found a solution to my problem. The reason Apollo-wrapped descendants of mounted components were causing problems is that they would throw an error when trying to access this.context.client. Apollo's MockedProvider creates an Apollo client (or optionally uses one you provide) and makes it available to its children via context.
Turns out Enzyme's mount method allows you to specify the component's context. I had tried using that before, but didn't realize that I also needed to combine it with the childContextTypes for that context to be passed down to the mounted component's descendants. Using these Enzyme options avoids the need to use the MockProvider.
I'll demonstrate the solution based off the example provided in my original question:
import React from 'react'
import { mount } from 'enzyme'
// This is an Apollo client I configured in a separate module
// with a mocked network interface. I won't go into details on
// that here, but am happy to provide more details if someone asks
import mockedClient from 'test/mocked_client'
// This component has descendants that are wrapped in Apollo and
// thus need access to `this.context` provided by an Apollo provider
import Assignments from 'app/components/assignments
...
describe('<Assignments />', function() {
it('sets sorted assignments in initial state', function() {
const assignments = [...]
const wrapper = mount(
<Assignments assignments={assignments} />,
{
context: { client: mockedClient },
childContextTypes: {
client: React.PropTypes.object.isRequired
}
}
)
// This now passes!
expect(wrapper.state('assignments')).to.eql([...])
})
})
Hopefully this helps someone who finds themselves in a similar situation!

How to you test a redux dumb component that renders smart components?

A "dumb" React view can be testing by passing it props before rendering it with enzyme/jsdom. Snapshot testing can then be used to validate the behavior (as in jest).
A smart component composed of a 'dumb view' connected with mapStateToProps/mapDispatchToProps can be tested by:
unit testing mapXxxToProps to check the logic is right
testing the dumb view
and optionnaly:
render the smart component by wrapping it in a Provider
However, this seems to break whenever a dumb view starts to nest other smart containers ; the rendering of the dumb view is not possible simply with props, since some child components down the chain might need a store.
Is there a way around that ?
Am I missing something ?
With enzyme, you can use a shallow render to test the dumb component is rendering the expected smart components, without actually rendering them completely.
Component (Bar is a smart component):
const Foo = () => {
return (
<div>
<Bar />
</div>
)
}
Test:
import { shallow } from 'enzyme';
...
it('should render <Foo /> component', () => {
const wrapper = shallow(<Foo />)
expect(wrapper.contains(<Bar />)).to.be.true
})

How can I find all nested Components using React/Redux?

I am looking to validate a form with Redux. I am trying to use make a form component which will iterate through children and find various input components (not to be confused with a native <input>.
I know there are a lot of open source solutions, but I'd like to understand some mechanics before jumping into picking any. I have a Form component setup to test like this:
import React from 'react';
export default class Component extends React.Component {
componentDidMount() {
this._iterate(this.props.children);
}
render(){
return (
<form {...this.props}>{this.props.children}</form>
);
}
_iterate(children) {
React.Children.forEach(children, child => {
console.log(child);
if (child.props.children) {
console.log('get children');
this._iterate(child.props.children);
}
});
}
};
I then have another Component with a render like this:
render() {
return (
<div>
<Form>
<ComponentA />
<ComponentB />
</Form>
</div>
);
}
Now ComponentA or ComponentB might have a component that nests more components down the line. Within those components would be a React component I have made for Text, Select, etc.
The code above would just console.log the components, and any children of them, that are in this specific render. It does not jump down into ComponentA children.
Is there a solution to that?
This isn't a problem you really want to solve.
The power in react is largely around the design pattern it encourages, and what you're doing is breaking that pattern; Component's should only talk to their immediate children and respond to their immediate parents. If you need to go deeper than that, then the component in the middle needs to be responsible for passing that data.
Rather than trying to dig into the innards of ComponentA and ComponentB, those component's themselves should have the accessibility props that you need. I.e., <ComponentA onChange={whatever} errorMessage={whatever}/> etc. and then hooking those props to their children should occur within ComponentA.

Resources