Reputation: 1465
// MyComponent.jsx
const MyComponent = (props) => {
const { fetchSomeData } = props;
useEffect(()=> {
fetchSomeData();
}, []);
return (
// Some other components here
)
};
// MyComponent.react.test.jsx
...
describe('MyComponent', () => {
test('useEffect', () => {
const props = {
fetchSomeData: jest.fn(),
};
const wrapper = shallow(<MyComponent {...props} />);
// THIS DOES NOT WORK, HOW CAN I FIX IT?
expect(props.fetchSomeData).toHaveBeenCalled();
});
});
When running the tests I get:
expect(jest.fn()).toHaveBeenCalled()
Expected mock function to have been called, but it was not called.
The expect fails because shallow
does not call useEffect. I cannot use mount because of other issues, need to find a way to make it work using shallow
.
Upvotes: 21
Views: 54444
Reputation: 2257
Another solution would be mocking useEffect
to call its first argument when called:
jest.mock('react', () => ({
...jest.requireActual('react'),
useEffect: f => f(),
}));
Upvotes: 0
Reputation: 8505
shallow
doesn't run effect hooks in React by default (it works in mount though) but you could use jest-react-hooks-shallow
to enable the useEffect and useLayoutEffect hooks while shallow mounting in enzyme.
Then testing is pretty straightforward and even your test specs will pass.
Here is a link to a article where testing the use-effect hook has been clearly tackled with shallow mounting in enzyme
https://medium.com/geekculture/testing-useeffect-and-redux-hooks-using-enzyme-4539ae3cb545
So basically with jest-react-hooks-shallow for a component like
const ComponentWithHooks = () => {
const [text, setText] = useState<>();
const [buttonClicked, setButtonClicked] = useState<boolean>(false);
useEffect(() => setText(
`Button clicked: ${buttonClicked.toString()}`),
[buttonClicked]
);
return (
<div>
<div>{text}</div>
<button onClick={() => setButtonClicked(true)}>Click me</button>
</div>
);
};
you'd write tests like
test('Renders default message and updates it on clicking a button', () => {
const component = shallow(<App />);
expect(component.text()).toContain('Button clicked: false');
component.find('button').simulate('click');
expect(component.text()).toContain('Button clicked: true');
});
Upvotes: 3
Reputation: 39025
I'm following this advice and using mount()
instead of shallow()
. Obviously, that comes with a performance penalty, so mocking of children is advised.
Upvotes: 1
Reputation: 409
Here's a solution from a colleague of mine at CarbonFive: https://blog.carbonfive.com/2019/08/05/shallow-testing-hooks-with-enzyme/
TL;DR: jest.spyOn(React, 'useEffect').mockImplementation(f => f())
Upvotes: 13
Reputation: 7990
useEffect is not supported by Enzyme's shallow rendering. It is on the roadmap (see column 'v16.8+: Hooks') to be fixed for the next version of Enzyme, as mentioned by ljharb
What you're asking is not possible with the current setup. However, a lot of people are struggling with this.
I've solved / worked around this by:
Here's a summary on how to mock modules, based on Mock Modules from the React docs.
contact.js
import React from "react";
import Map from "./map";
function Contact(props) {
return (
<div>
<p>
Contact us via [email protected]
</p>
<Map center={props.center} />
</div>
);
}
contact.test.js
import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import Contact from "./contact";
import MockedMap from "./map";
jest.mock("./map", () => {
return function DummyMap(props) {
return (
<p>A dummy map.</p>
);
};
});
it("should render contact information", () => {
const center = { lat: 0, long: 0 };
act(() => {
render(
<Contact
name="Joni Baez"
email="[email protected]"
site="http://test.com"
center={center}
/>,
container
);
});
});
Useful resources:
Upvotes: 13