I am writing an simple app where after clicking the button, async call to spotify API should be performed and when promise resolves it should update component's state. I am using react hooks to manage state in my component.
In my tests I mocked API call.
spotify.jsx
export default class Spotify {
constructor(token) {
this.axiosInstance = axios.create({
baseURL: baseURL,
headers: buildHeaders(token),
});
}
async getUserInfo() {
const userInfo = await this.axiosInstance({
url: `/me`,
});
return userInfo.data
}
}
spotify mock:
const getUserInfoMock = jest.fn();
const mock = jest.fn().mockImplementation(() => ({
getUserInfo: getUserInfoMock,
}));
export default mock;
User.jsx
const User = props => {
const [user, setUser] = useState(null);
const {token} = useContext(AuthContext);
const spotify = useMemo(() => new Spotify(token), [token]);
const getUserInfo = async () => {
console.log("button clicked")
const fetched = await spotify.getUserInfo();
console.log(fetched)
setUser(fetched);
}
return (
<React.Fragment>
<p>user page</p>
<button onClick={getUserInfo} > click me </button>
{user && (
<div>
<p>{user.display_name}</p>
<p>{user.email}</p>
</div>
)}
</React.Fragment>
);
};
My question is how to properly test such behavior. I managed to make it pass but isn't calling await
on simulate()
an ugly hack? Simulate does not return a promise. Here is a test:
it('updates display info with data from api', async () => {
const userInfo = {
display_name: 'Bob',
email: '[email protected]',
};
spotifyMock.getUserInfo.mockImplementation(() => Promise.resolve(userInfo));
wrapper = mount(<User />);
expect(wrapper.find('p')).toHaveLength(1);
await wrapper
.find('button')
.last()
.simulate('click');
wrapper.update();
expect(wrapper.find('p')).toHaveLength(3);
});
On the other hand when i check only if mock was called I don't need to use async/await and test passes:
it('calls spotify api on click', () => {
wrapper = mount(<User />);
expect(spotifyMock.getUserInfo).not.toHaveBeenCalled();
wrapper
.find('button')
.last()
.simulate('click');
expect(spotifyMock.getUserInfo).toHaveBeenCalledTimes(1);
});
I wonder if my way of testing is proper and what if I want to add a feature to fetch data from api when component renders - with useEffect hook. Does Enzyme has a full support for react hooks?
I also struggle with warning Warning: An update to User inside a test was not wrapped in act(...)
even if I wrap a mount
and simulate
functions.
You should wrap your render-affecting calls in an async act() function as per Dan Abramov's blog post like so:
it('calls spotify api on click', async () => {
await act(async () => {
wrapper = mount(<User />);
});
expect(spotifyMock.getUserInfo).not.toHaveBeenCalled();
await act(async () => {
wrapper
.find('button')
.last()
.simulate('click');
});
wrapper.update();
expect(spotifyMock.getUserInfo).toHaveBeenCalledTimes(1);
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With