I have a list component to display data fetched from a REST API. I do the fetching in a useEffect hook with no dependency array, and set the result into my state. This is my code:
import { useEffect, useState } from 'react';
import { fetchAllMedias } from '../service';
import { MediaResponse } from '../types';
import { MediaType } from '../enums';
export default function MediaList() {
const [medias, setMedias] = useState<MediaResponse[]>([]);
useEffect(() => {
const setMediasOnLoad = async () => {
try {
const fetchedMedias = await fetchAllMedias();
setMedias(fetchedMedias);
} catch (error) {
console.error('error fetching medias:', error);
}
};
void setMediasOnLoad();
}, []);
const getMediaTypeIcon = (mediaType: MediaType) => {
/* returns an Icon component */
};
return (
<div id='media-list'>
{medias.map((media) => (
<div key={media.mediaId} className='grid grid-cols-12 border-b-2 first:border-t-2 py-2 gap-4'>
<div className='col-span-6 pl-4 flex gap-2'>
<div className='flex items-center'>{getMediaTypeIcon(media.mediaType)}</div>
<span>{media.title}</span>
</div>
<div className='border border-violet-200 bg-violet-200 rounded-md font-semibold text-violet-700 px-2'>
{media.mediaLanguage.mediaLanguageName}
</div>
<div className='border border-emerald-200 bg-emerald-200 rounded-md font-semibold text-emerald-700 px-2'>
{media.country.countryName}
</div>
</div>
))}
</div>
);
}
I would like to write a unit test to make sure that the list can be rendered properly, e.g. checking that the title of a movie can be shown. I have provided a mock implementation for the fetchAllMedias function, and I have asserted that the function was called. However, the list does not render in the test. This is how I have written the test:
import { render, screen, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
import * as service from '../service';
import MediaList from '.';
const mockMedias = [
/* mock data */
];
test('Can render media list', async () => {
const mockFetchMedias = vi.spyOn(service, 'fetchAllMedias');
mockFetchMedias.mockReturnValue(new Promise(() => mockMedias));
render(<MediaList />);
expect(mockFetchMedias).toHaveBeenCalled();
expect(mockFetchMedias).toReturnWith(new Promise(() => mockMedias));
await waitFor(() => {
expect(screen.getByText(/Avengers/)).toBeInTheDocument();
});
});
When running vitest, I get the following error:
TestingLibraryElementError: Unable to find an element with the text: /Avengers/. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
Ignored nodes: comments, script, style
<body>
<div>
<div
id="media-list"
/>
</div>
</body>
I am new to vitest (and frontend testing in general). I have searched for a long time how to fix this issue, but I haven't been able to find others with the same problem. Any help with this will be greatly appreciated.
Found the issue! The issue was with the line
mockFetchMedias.mockReturnValue(new Promise(() => mockMedias));.There is another function,
mockResolvedValue, that should be used instead for async functions. So I changed the above line tomockFetchMedias.mockResolvedValue(mockMedias);and the test is passing now!
I also moved the
renderfunction into thewaitForblock, and removed the assertion outside instead: