Reputation: 1594
I am using jest/istanbul to track code coverage. I have the following component:
// Menu.jsx
const myComponent = ({ initialState }) = {
const [state, setState] = useState(initialState);
const [menuContent, setMenuContent] = useState(undefined);
return (
<Menu>
{state.accordions.map(item, index) => (
<MenuPanel id={item.id}>
{item.details && typeof item.details === 'function'
? <item.details setContent={myContent => setMenuContent(myContent)} />
: undefined}
</MenuPanel>
)}
</Menu>
)
}
In my test for Menu.jsx
the jest coverage report is complaining that setMenuContent
is not covered in my tests. How am I supposed to test a hook like that? I thought it wasn't possible. I tried testing that the setContent
prop exists on the subcomponent but that didn't help. Any ideas on how to get this to pass the coverage report? I am using shallow rendering in my test.
Upvotes: 0
Views: 643
Reputation: 5849
You can mock useState for this specific component, try this:
const stateMock = jest.fn();
jest.mock('react', () => {
const ActualReact = require.requireActual('react');
return {
...ActualReact,
useState: () => ['value', stateMock], // what you want to return when useContext get fired goes here
};
});
Everytime your component calls useState
your stateMock
will be fired and your case will be covered
This is just a minimal example of what you can do, but you can enhance the mock to recognize each state call
If you want to keep the default behaviour of React state you can declare your callback function outside of the component body, in your case the concerned line of code is here:
<item.details setContent={myContent => setMenuContent(myContent)} />
So instead of this anonymous function which can lead to memory leak anyways, you can take it out of your component and do something like so:
const setContent = (setMenuContent) => (myContent) => setMenuContent(myContent)
const myComponent = ({ initialState }) = {
const [state, setState] = useState(initialState);
const [menuContent, setMenuContent] = useState(undefined);
return (
<Menu>
{state.accordions.map(item, index) => (
<MenuPanel id={item.id}>
{item.details && typeof item.details === 'function'
? <item.details setContent={setContent(setMenuContent)} />
: undefined}
</MenuPanel>
)}
</Menu>
)
}
Now you can export this function and cover it with a text, which will allow you to mock setMenuContent
export const setContent = (setMenuContent) => (myContent) => setMenuContent(myContent)
Your last option is to use something like enzyme or react-testing-lib, find this item.details
component in the dom and trigger a click action
Upvotes: 1