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.
Related
Say I have a simple React functional component that largely just observes changes in context and renders some view of the state from the context.
export default function Observer(props) {
// I have a separate dispatch context for performance. This component doesn't dispatch
const state = useContext(SomeContext);
return (
<div>Foo is now {state.foo}</div>
)
}
For testing under jest/react testing library, I was imagining a few possibilities:
Wire up a wrapper for the context that just instantiates different
states for each test. This seems like it would be easy and straightforward. Is this
the typical pattern?
Instantiate each test with real initial state.
Then somehow change state and expect the component to update.
Perhaps using dispatch from the test code somehow. Most examples I see fire events, but this
component doesn't dispatch anything, it just observes.
Render a larger part of the app, with other components that update the state.
This seems like a bad choice for a unit test of a small, simple
component.
The first option is the way to go. As you already mentioned, the component in question is a consumer of the context. So, if it is provided with some value for context, it will most certainly use it.
You can create a simple function to pass different context values, which is provided via provider and is rendered. Then you assert what happens,
const renderComponent = (contextValue) => {
render(
<SomeContextProvider value={contextValue}>
<Observer />
</SomeContextProvider>
);
};
test('my test case name', () => {
render({foo: abc});
expect(screen.getByText('Foo is now abc')).toBeInTheDocument();
})
Some good reading here https://testing-library.com/docs/example-react-context/
Learning testing with React, and I came across this point from this article: https://reactjs.org/docs/shallow-renderer.html
When writing unit tests for React, shallow rendering can be helpful.
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.
Can someone explain the concept behind "levels deep"? Does this have any relation to objects and how many levels deep they are?
The depth of a component is the number of children and grand children it has:
const Level1Component = () => <Component /> // No children
const Level2Component = ({children}) => <Component>{children}</Component>
And if it's children have children, then it's depth = 3 and so on.
It means when testing a component with another component inside, you can just focus on the target component you want to test, and ignore the contents of it's child, grandchild, ... etc components.
For example, now I want to test a component called <ParentComponent>, which has a <ChildComponent /> inside :
<div>
<h1>Hi</h1>
<ChildComponent />
</div>
When testing with <ParentComponent />, it will not render the JSX part of <ChildComponent />, so the testing code can be more simple and pure:
import ShallowRenderer from 'react-test-renderer/shallow';
// in your test:
const renderer = new ShallowRenderer();
renderer.render(<ParentComponent />);
const result = renderer.getRenderOutput();
expect(result.type).toBe('div');
expect(result.props.children).toEqual([
<h1>Hi</h1>,
<ChildComponent /> // don't need to worry about what's inside
]);
( this example is similar to the React Doc )
It is saying you can check the state of your parent-component with out rendering your child component state/data by just rendering the component. That make sense? One-level in this case the top level / shallow.
<parent-component>
{parent-data}
<child-component-1>{child-1-data}</child-component-1>
<child-component-2>{child-2-data}</child-component-2>
</parent-component>
I am still fairly new to Reactjs and I am struggling with one thing.
I have built a small React app that consists of:
App.js at the very top –the only class component that controls all the states
3 stateless components ( no1 , no2, no3 rendered in App.js (all with smaller components inside them, but that’s irrelevant)
App.js state includes 3 arrays all of which are used by those 3 stateless components rendered.
And then, in App.js I have around 400 lines of methods where I am modifying state by calling setState. That’s quite long....
My question is: is there anyway to split this file? To move methods to their respective components: no 1, 2 and 3.
It seems impossible to me as having all the states in one class requires calling setState (having setState methods) in the same class only.
This might be a stupid question but:
Is it possible to modify state outside parent that holds this state (App.js), for instance, modify it in a stateless component no 1, and still keep parent updated about the change so that it can inform stateless components no 2 and no 3 about the change.
What’s the best practice in my case?
thanks
What i do for code/logic splitting is:
Keep the App.js as simple as possible
If there is a lot of logic in a file/Component, i split the file to index.js and Component.js and keep the logic in index.js and the JSX/HTML in the Component.js
In your case you can:
Remove all logic from App.js
Create 3 folders no1, no2 and no3
Inside each folder create two files - index.js and ComponentNo1.js etc
In the index.js create a Component with state and in ComponentNo1.js create a stateless component that returns the JSX/HTML.
This is what I would recommend. If you find it hard to do it, post your App.js logic so that I could help you do it.
Sure something like this is completely valid and is a common React practice.
I've written a sandbox to give you an idea of how this works:
https://codesandbox.io/s/zxl2owp2np
But to explain in detail, let's assume you have the following components:
class App extends React.Component {
state = {
value: 5
};
increaseValue = () => {
this.setState({
value: this.state.value + 1
});
};
render() {
return (
<div className="App">
<Example1 value={this.state.value} increaseValue={this.increaseValue} />
<Example2 value={this.state.value} />
<Example3 value={this.state.value} />
</div>
);
}
}
const Example1 = (props) => {
const value = props.value;
return (
<div>
I am Example1. The value is {value}
<button onClick={() => props.increaseValue()}>Click</button>
</div>
);
}
const Example2 = (props) => {
const value = props.value;
return (
<div>
I am Example2. The value is {value}
</div>
);
}
const Example3 = (props) => {
const value = props.value;
return (
<div>
I am Example3. The value is {value}
</div>
);
}
Our main App component has a state value of 5 that we pass into our 3 child components. The child components are all using that value to display data. In the App component, we defined a state-updating function called increaseValue, which we pass in to Example1 as property. Now Example1 can use increaseValue by calling props.increaseValue()
props.increaseValue() is bound to our App component's execution context. So it will update the value in App's component state. Now with the updated value, it gets shared across all our Example components for use.
Regarding your question about moving methods to the components that are using them. This is not a common practice. Typically you define the function inside the component where the state is going to be updated. Meaning, if you have a function that is meant to update the App component, it should be defined in the app component.
I made two versions of the same simple test and I am looking for the better one. The only difference is that the first returns HTML code <div>::name::</div> and the second one - JSX <div>{name}</div>. I believe the goal of this kind of render test is to prove that the correct HTML is being returned from the render, so I believe the first approach is more accurate. Thoughts?
version 1:
const prop = {name: '::name::'}
const wrapper = shallow(<Component prop={prop} />)
expect(wrapper.equals(
<div>
<div>::name::</div>
</div>
)).toEqual(true)
version 2:
const name = ::name::
const wrapper = shallow(<Component prop={name: name} />)
expect(wrapper.equals(
<div>
<div>{name}</div>
</div>
)).toEqual(true)
First thumb rule in React unit testing - check only the conditionals.
What that means is, when you pass a prop and just render the prop in a div, it will be rendered. You don't have to test if the same value you pass is rendered in the div because that is the library behaviour and React is already tested to render props values properly.
Instead, your test cases should focus on conditionals and your logic. For example
const Example = ({ isFoo }) => {
return(
<div>{isFoo ? <Foo /> : <Bar />}</div>
);
};
Now this should be unit tested as
const wrapper = shallow(<Example isFoo={true} />);
expect(wrapper.contains('Foo')).toEqual(true);
expect(wrapper.contains('Bar')).toEqual(false);
Note: syntax might vary but you get the gist.
There is no big difference since
::name:: is still hardcoded
.equals() does not check if it's a reference to the same variable.
So there is still no way to validate if name is coming from props and not hard-coded equally to testing value.
It's better just update props in your test and ensure component is changed:
wrapper.setProps({prop: {name: 'anotherName'}});
expect(wrapper).toMatchSnapshot();
BTW you better rely on toMatchSnapshot() instead of comparing manually.
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()