Skip to content

Instantly share code, notes, and snippets.

@mustafadalga
Created November 18, 2023 11:17
Show Gist options
  • Save mustafadalga/2dfe2784e9fde40535a21911e375997e to your computer and use it in GitHub Desktop.
Save mustafadalga/2dfe2784e9fde40535a21911e375997e to your computer and use it in GitHub Desktop.
Unit Testing React Hooks with MSW, Vitest, and Testing Library: Fetching and Updating Posts
import { expect, describe, it, beforeAll, afterEach, afterAll, beforeEach } from "vitest";
import { QueryClientProvider } from "react-query";
import { ReactNode } from "react";
import { http, HttpResponse } from "msw";
import { useCommonStore } from "@/_store/useCommonStore";
import { act, renderHook, waitFor } from "@testing-library/react";
import { createTestServer, queryClient } from "@/__tests__/utilities";
import useFetchCustomVariables from "@/_hooks/useFetchCustomVariables";
const successResponse = [
"post 1",
"post 2",
]
const url = "/api/posts"
const server = createTestServer([
{
url,
method: "GET",
successResponse
}
]);
beforeAll(() => server.listen());
beforeEach(() => {
queryClient.resetQueries();
const { result } = renderHook(() => useCommonStore());
act(() => result.current.reset())
});
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
const wrapper = ({ children }: { children: ReactNode }) => (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);
describe("useFetchCustomVariables", () => {
it('should fetch the list of the post', async () => {
const { result } = renderHook(() => useFetchCustomVariables(), { wrapper });
await waitFor(() => result.current.posts.length > 0);
expect(result.current.posts).toEqual(successResponse);
});
it('should fetches and updates api status in store', async () => {
const { result: resultStore } = renderHook(() => useCommonStore());
expect(resultStore.current.apiStatuses.posts).toBe(undefined)
const { result } = renderHook(() => useFetchCustomVariables(), { wrapper });
expect(resultStore.current.apiStatuses.posts.isCalled).toBe(true)
await waitFor(() => result.current.posts.length > 0);
expect(resultStore.current.apiStatuses.posts.isLoaded).toBe(true)
});
it("should handle API failure gracefully and update store accordingly", async () => {
server.use(
http.get(url, () => {
return HttpResponse.json({
message: "Interval Error"
}, { status: 500 })
}),
);
const { result } = renderHook(() => useFetchCustomVariables(), { wrapper });
const { result: resultStore } = renderHook(() => useCommonStore());
await waitFor(() => resultStore.current.apiStatuses.posts.isLoaded);
expect(resultStore.current.apiStatuses.posts.isCalled).toBe(true)
expect(resultStore.current.apiStatuses.posts.isLoaded).toBe(true)
expect(result.current.posts).toEqual([]);
})
})
import { useQuery } from "react-query";
import axios from "axios";
import { useCommonStore } from "@/_store/useCommonStore";
import { useEffect } from "react";
const fetchVariables = async (): Promise<string[]> => {
const { data } = await axios.get<string[]>('/api/posts');
return data;
};
export default function useFetchCustomVariables(): { posts: string[] } {
const setApiStatus = useCommonStore(state => state.setApiStatus);
const {
data: posts = [],
} = useQuery<string[]>([ 'posts' ], fetchVariables, {
staleTime: Infinity,
onSettled: () => {
setApiStatus("posts", {
isLoaded: true,
});
}
}
);
useEffect(() => {
setApiStatus("posts", {
isCalled: true,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return {
posts,
}
}
import { setupServer } from 'msw/node';
import { http, HttpResponse } from 'msw';
import { QueryClient } from "react-query";
export interface ServerOptions {
url: string,
method: "POST" | "GET",
successResponse?: {
[key: string]: any
},
status?: number
}
export function createTestServer(optionsArray: ServerOptions[]) {
const handlers = optionsArray.map(({ url, method, successResponse = {}, status = 200 }) => {
return (
method === 'GET'
? http.get(url, () => HttpResponse.json(successResponse, { status }))
: http.post(url, () => HttpResponse.json(successResponse, { status }))
);
});
return setupServer(...handlers);
}
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment