T
TanStack•3y ago
plain-purple

Testing useQuery by mocking it's callback

Hello 👋 I want to test my component by mocking the callback used by useQuery To do so I use Jest's spyOn function, which should spy on the named exported function and should use the mocked implementation if given. Well, it should but it don't I tried several things (can't enumerate them since I've tried a lot of "solutions" 😅 ) Here is code snippet for your help (in comment)
2 Replies
plain-purple
plain-purpleOP•3y ago
api/todos.ts
import { TodoDTO } from "@common/types";

export const fetchTodos = async (): Promise<TodoDTO[]> => {
throw new Error("Not implemented");
};
import { TodoDTO } from "@common/types";

export const fetchTodos = async (): Promise<TodoDTO[]> => {
throw new Error("Not implemented");
};
App.tsx
import { DotsThree } from "@phosphor-icons/react";
import { FC } from "react";
import { useQuery } from "react-query";

import { fetchTodos } from "./api/todos";
import { TodoList } from "./components/todos/list";

export const App: FC = () => {
const { data: todos, error: todosError } = useQuery("todos", fetchTodos);

return (
<main className={"flex min-h-screen items-center"}>
<div className={"mx-auto w-full max-w-6xl"}>
{todosError ? (
<div
className={
"mb-4 flex items-center rounded-xl border border-red-300 bg-red-50 p-4 text-sm text-red-800"
}
data-testid={"error"}
role={"alert"}
>
<p className={"font-medium"}>
{todosError instanceof Error
? todosError.message
: "An error occurred"}
</p>
</div>
) : !todos ? (
<div className={"flex h-96 items-center justify-center"}>
<DotsThree
className={"h-12 w-12 animate-bounce"}
data-testid={"spinner"}
/>
</div>
) : !todos.length ? (
<div className={"flex h-96 items-center justify-center"}>
<p className={"text-2xl"}>No todo list</p>
</div>
) : (
<TodoList todos={todos} />
)}
</div>
</main>
);
};
import { DotsThree } from "@phosphor-icons/react";
import { FC } from "react";
import { useQuery } from "react-query";

import { fetchTodos } from "./api/todos";
import { TodoList } from "./components/todos/list";

export const App: FC = () => {
const { data: todos, error: todosError } = useQuery("todos", fetchTodos);

return (
<main className={"flex min-h-screen items-center"}>
<div className={"mx-auto w-full max-w-6xl"}>
{todosError ? (
<div
className={
"mb-4 flex items-center rounded-xl border border-red-300 bg-red-50 p-4 text-sm text-red-800"
}
data-testid={"error"}
role={"alert"}
>
<p className={"font-medium"}>
{todosError instanceof Error
? todosError.message
: "An error occurred"}
</p>
</div>
) : !todos ? (
<div className={"flex h-96 items-center justify-center"}>
<DotsThree
className={"h-12 w-12 animate-bounce"}
data-testid={"spinner"}
/>
</div>
) : !todos.length ? (
<div className={"flex h-96 items-center justify-center"}>
<p className={"text-2xl"}>No todo list</p>
</div>
) : (
<TodoList todos={todos} />
)}
</div>
</main>
);
};
App.test.tsx:
import { render, screen, waitFor } from "@testing-library/react";

import * as todos from "./api/todos";
import { App } from "./App";
import { TestingProviders } from "./tests/provider";

describe("App", () => {
afterEach(() => {
jest.resetAllMocks();
});

it("should render", () => {
const mockedFetchTodos = jest.spyOn(todos, "fetchTodos");

mockedFetchTodos.mockImplementation(async () => {
return new Promise(() => {});
});

render(
<TestingProviders>
<App />
</TestingProviders>,
);

expect(true).toBe(true);
});

it("should show loading", async () => {
const mockedFetchTodos = jest.spyOn(todos, "fetchTodos");

mockedFetchTodos.mockImplementation(async () => {
return new Promise(() => {});
});

render(
<TestingProviders>
<App />
</TestingProviders>,
);

await waitFor(() => expect(mockedFetchTodos).toHaveBeenCalled());

const spinner = screen.getByTestId("spinner");

expect(spinner).toBeInTheDocument();
});

it("should show error", async () => {
const mockedFetchTodos = jest.spyOn(todos, "fetchTodos");

mockedFetchTodos.mockImplementation(async () => {
throw new Error("My error");
});

render(
<TestingProviders>
<App />
</TestingProviders>,
);

await waitFor(() => expect(mockedFetchTodos).toHaveBeenCalled());

const error = screen.getByTestId("error");

expect(error).toBeInTheDocument();
expect(error).toHaveTextContent("My error");
});
});
import { render, screen, waitFor } from "@testing-library/react";

import * as todos from "./api/todos";
import { App } from "./App";
import { TestingProviders } from "./tests/provider";

describe("App", () => {
afterEach(() => {
jest.resetAllMocks();
});

it("should render", () => {
const mockedFetchTodos = jest.spyOn(todos, "fetchTodos");

mockedFetchTodos.mockImplementation(async () => {
return new Promise(() => {});
});

render(
<TestingProviders>
<App />
</TestingProviders>,
);

expect(true).toBe(true);
});

it("should show loading", async () => {
const mockedFetchTodos = jest.spyOn(todos, "fetchTodos");

mockedFetchTodos.mockImplementation(async () => {
return new Promise(() => {});
});

render(
<TestingProviders>
<App />
</TestingProviders>,
);

await waitFor(() => expect(mockedFetchTodos).toHaveBeenCalled());

const spinner = screen.getByTestId("spinner");

expect(spinner).toBeInTheDocument();
});

it("should show error", async () => {
const mockedFetchTodos = jest.spyOn(todos, "fetchTodos");

mockedFetchTodos.mockImplementation(async () => {
throw new Error("My error");
});

render(
<TestingProviders>
<App />
</TestingProviders>,
);

await waitFor(() => expect(mockedFetchTodos).toHaveBeenCalled());

const error = screen.getByTestId("error");

expect(error).toBeInTheDocument();
expect(error).toHaveTextContent("My error");
});
});
genetic-orange
genetic-orange•2y ago
It's recommended to mock API's result and not the react-query hooks. Check it out: https://tkdodo.eu/blog/testing-react-query
Testing React Query
Let's take a look at how to efficiently test custom useQuery hooks and components using them.

Did you find this page helpful?