T
TanStack2y ago
like-gold

Mock mutateAsync in jest

I have this hook that I mock with MSW. The tests for the isolated hook work.
export const useEditUserProfile = () => {
return useMutation({
mutationFn: ({ data }: Payload) =>
request({
url: "user",
body: data,
method: "PATCH",
}),
});
};
export const useEditUserProfile = () => {
return useMutation({
mutationFn: ({ data }: Payload) =>
request({
url: "user",
body: data,
method: "PATCH",
}),
});
};
Writing tests for my component has been painful. It seems like I have to mock the entire implementation of mutateAsync. Is there a better way to do this?
export const Profile = () => {
const { mutateAsync: mutateUserProfile } = useEditUserProfile();

const onUpdateUserProfile = useCallback(
async (values: UserProfileForm) => {
await mutateUserProfile(
{ data: values },
{
onSuccess: res => {
console.log("res :>> ", res);
const currentUserData = user();

const updatedUserData = {
...currentUserData,
first_name: res.first_name,
last_name: res.last_name,
email: res.email,
};

setUser(updatedUserData);
setAlert({
message: "User profile updated!",
type: "success",
});
},
onError: () => {
setAlert({
message: "Uh-oh! Something went wrong!",
type: "error",
});
},
},
);
},
[mutateUserProfile, setAlert],
);
export const Profile = () => {
const { mutateAsync: mutateUserProfile } = useEditUserProfile();

const onUpdateUserProfile = useCallback(
async (values: UserProfileForm) => {
await mutateUserProfile(
{ data: values },
{
onSuccess: res => {
console.log("res :>> ", res);
const currentUserData = user();

const updatedUserData = {
...currentUserData,
first_name: res.first_name,
last_name: res.last_name,
email: res.email,
};

setUser(updatedUserData);
setAlert({
message: "User profile updated!",
type: "success",
});
},
onError: () => {
setAlert({
message: "Uh-oh! Something went wrong!",
type: "error",
});
},
},
);
},
[mutateUserProfile, setAlert],
);
1 Reply
like-gold
like-goldOP2y ago
this is the test. However, when I log res inside onSuccess, I don't get a value. Is there any resources as to how I can mock the values on onSuccess or onError ?
jest.mock("@libs/api/data-access", () => ({
apiUrl: () => "http://localhost:8000/api/user/",
useUserLogout: () => jest.fn(),
useEditUserProfile: () => ({
mutateAsync: jest.fn().mockImplementation(userData => {
const mockResponse = {
first_name: "Jerry",
last_name: "Seinfeld",
email: "jerryseinfeld@gmail.com",
};

return new Promise((resolve, reject) => {
resolve(mockResponse);
});
}),
}),
user: () => ({
first_name: "Elaine",
last_name: "Benes",
email: "elainebenes@gmail.com",
}),
}));

describe("Settings: Profile", () => {
it("updates user profile and shows success alert on successful update", async () => {
const mockSetAlert = jest.fn();
const { getByLabelText, getByRole } = render(<Profile />);

fireEvent.change(getByLabelText(/first name/i), {
target: { value: "Jerry" },
});
fireEvent.change(getByLabelText(/last name/i), {
target: { value: "Seinfeld" },
});
fireEvent.change(getByLabelText(/email/i), {
target: { value: "jerryseinfeld@gmail.com" },
});
fireEvent.click(getByRole("button", { name: /update/i }));

await waitFor(() => {
expect(mockSetAlert).toHaveBeenCalledWith({
message: "User profile updated!",
type: "success",
});
});
});
});
jest.mock("@libs/api/data-access", () => ({
apiUrl: () => "http://localhost:8000/api/user/",
useUserLogout: () => jest.fn(),
useEditUserProfile: () => ({
mutateAsync: jest.fn().mockImplementation(userData => {
const mockResponse = {
first_name: "Jerry",
last_name: "Seinfeld",
email: "jerryseinfeld@gmail.com",
};

return new Promise((resolve, reject) => {
resolve(mockResponse);
});
}),
}),
user: () => ({
first_name: "Elaine",
last_name: "Benes",
email: "elainebenes@gmail.com",
}),
}));

describe("Settings: Profile", () => {
it("updates user profile and shows success alert on successful update", async () => {
const mockSetAlert = jest.fn();
const { getByLabelText, getByRole } = render(<Profile />);

fireEvent.change(getByLabelText(/first name/i), {
target: { value: "Jerry" },
});
fireEvent.change(getByLabelText(/last name/i), {
target: { value: "Seinfeld" },
});
fireEvent.change(getByLabelText(/email/i), {
target: { value: "jerryseinfeld@gmail.com" },
});
fireEvent.click(getByRole("button", { name: /update/i }));

await waitFor(() => {
expect(mockSetAlert).toHaveBeenCalledWith({
message: "User profile updated!",
type: "success",
});
});
});
});
There was a similar question asked two years ago https://stackoverflow.com/a/67111510. I partially agree with the answer. However, what if you wanted to test some behavior inside the onSuccess callback? In my case, I update the user value that is stored in localstorage. I have separate tests that test the behavior of updating localStorage variables. But they are considered "unit" rather than "integration" tests, which what the above is.

Did you find this page helpful?