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-purpleOP•3y ago
api/todos.ts
App.tsx
App.test.tsx:
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");
};
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>
);
};
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•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-queryTesting React Query
Let's take a look at how to efficiently test custom useQuery hooks and components using them.