Ive been working on test store creation and to do that I need to mock the return of my promise call
const mockAccounts = [
{
avatar: 'https://example.com/image1.jpg',
expiresAt: 'Fri May 17 2024 22:57:54 GMT-0300 (Brasilia Standard Time)',
generatedAt:
'Fri May 17 2024 22:57:54 GMT-0300 (Brasilia Standard Time)',
id: 1,
socialMediaId: 'DISCORD_EXAMPLE_ID',
token: 'DISCORD_EXAMPLE_TOKEN_1',
userName: 'Discord User 1',
valid: true,
},
{
avatar: 'https://example.com/image2.jpg',
expiresAt: 'Fri May 17 2024 22:57:54 GMT-0300 (Brasilia Standard Time)',
generatedAt:
'Fri May 17 2024 22:57:54 GMT-0300 (Brasilia Standard Time)',
id: 14,
socialMediaId: 'TWITTER_EXAMPLE_ID',
token: 'TWITTER_EXAMPLE_TOKEN_14',
userName: 'Twitter User 14',
valid: true,
},
];
AccountsService.fetchAll.mockResolvedValueOnce(mockAccounts);
The thing is that I initialize my store creation with a function call
const getAllAccounts = async (): Promise<void> => {};
void getAllAccounts();
when I start my store no mocks that Ive tried worked the way I wanted, all of them simply don’t mock anything when I use the renderHook method
const { result } = renderHook(() => useSocialMediaStore());
But when I run my method manually the mock works as expected
await act(async () => {
await result.current.getAllAccounts?.();
});
I think my error is maybe how Im starting my store creation or how Im mocking it, but Im not sure where my mistake on this step and everything that I tried didnt worked
Does anyone know how to fix this?
If anyone needs more context, a more detailed look:
My Store:
/* eslint-disable @typescript-eslint/no-empty-function */
import { create } from 'zustand';
import { SocialMediaService } from '~services/api/social-media/social-media';
import { SocialMedia } from '~services/api/social-media/social-media.types';
import { MultiMap } from '~utils/multimap/multimap';
import { AccountsService } from '../../services/api/accounts/accounts';
import { SocialMediaState, StoreAccount } from './useSocialMediaStore.types';
export const useSocialMediaStore = create<SocialMediaState>((set) => {
const accounts = {
data: null,
error: '',
loading: false,
};
const socialMedias = new Map<string, SocialMedia>();
const addAccount = async (): Promise<void> => {};
const getAllAccounts = async (): Promise<void> => {
set((state) => ({ accounts: { ...state.accounts, loading: true } }));
const fetchedAccounts = await AccountsService.fetchAll();
const userSocialMedias = new Set<string>();
const accountsBySocialMedia = new MultiMap<StoreAccount>();
for (const account of fetchedAccounts) {
userSocialMedias.add(account.socialMediaId);
accountsBySocialMedia.add(account.socialMediaId, {
...account,
valid: false,
});
}
const fetchedSocialMedias = await SocialMediaService.fetch(
Array.from(userSocialMedias)
);
const fetchedSocialMediasMap = new Map<SocialMedia['id'], SocialMedia>();
for (const socialMedia of fetchedSocialMedias) {
fetchedSocialMediasMap.set(socialMedia.id, socialMedia);
}
set(() => ({ socialMedias: fetchedSocialMediasMap }));
set(() => ({
accounts: {
data: accountsBySocialMedia.toArray(),
error: '',
loading: false,
},
}));
};
void getAllAccounts();
return {
accounts,
addAccount,
getAllAccounts,
socialMedias,
};
});
My test:
import { act, renderHook } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import { useSocialMediaStore } from '~stores/useSocialMediaStore/useSocialMediaStore';
import { AccountsService } from '../../services/api/accounts/accounts';
vi.mock('../../services/api/accounts/accounts', () => ({
AccountsService: {
fetchAll: vi.fn(),
},
}));
describe.only('useSocialMediaStore', () => {
it('Checks if the function correctly handles input and produces expected output', async () => {
const mockAccounts = [
{
avatar: 'https://example.com/image1.jpg',
expiresAt: 'Fri May 17 2024 22:57:54 GMT-0300 (Brasilia Standard Time)',
generatedAt:
'Fri May 17 2024 22:57:54 GMT-0300 (Brasilia Standard Time)',
id: 1,
socialMediaId: 'DISCORD_EXAMPLE_ID',
token: 'DISCORD_EXAMPLE_TOKEN_1',
userName: 'Discord User 1',
valid: true,
},
{
avatar: 'https://example.com/image2.jpg',
expiresAt: 'Fri May 17 2024 22:57:54 GMT-0300 (Brasilia Standard Time)',
generatedAt:
'Fri May 17 2024 22:57:54 GMT-0300 (Brasilia Standard Time)',
id: 14,
socialMediaId: 'TWITTER_EXAMPLE_ID',
token: 'TWITTER_EXAMPLE_TOKEN_14',
userName: 'Twitter User 14',
valid: true,
},
];
AccountsService.fetchAll.mockResolvedValueOnce(mockAccounts);
const { result } = renderHook(() => useSocialMediaStore());
await act(async () => {
await result.current.getAllAccounts?.();
});
expect(result.current.accounts.data).toEqual([
{
avatar: 'https://example.com/image1.jpg',
expiresAt: 'Fri May 17 2024 22:57:54 GMT-0300 (Brasilia Standard Time)',
generatedAt:
'Fri May 17 2024 22:57:54 GMT-0300 (Brasilia Standard Time)',
id: 1,
socialMediaId: 'DISCORD_EXAMPLE_ID',
token: 'DISCORD_EXAMPLE_TOKEN_1',
userName: 'Discord User 1',
valid: false,
},
{
avatar: 'https://example.com/image2.jpg',
expiresAt: 'Fri May 17 2024 22:57:54 GMT-0300 (Brasilia Standard Time)',
generatedAt:
'Fri May 17 2024 22:57:54 GMT-0300 (Brasilia Standard Time)',
id: 14,
socialMediaId: 'TWITTER_EXAMPLE_ID',
token: 'TWITTER_EXAMPLE_TOKEN_14',
userName: 'Twitter User 14',
valid: false,
},
]);
});
});
The problem is initializing the store when using renderHook, as the AccountsService.fetchAll service mock is not ready in time. I believe that the solution is related to configuring the mock before initializing the store and waiting for the asynchronous updates to complete. This ensures that the test synchronizes correctly with the store’s asynchronous execution.
João Burgarell is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.