I am trying to mock my axios get in Jest. I'm new to testing in react so please be kind
I've followed this tutorial and this answer, I think i've got a grip of what to do. Basically on component mounting, I called 2 data fetching on my UseEffect
.
Here's the not working test:
jest.mock("axios");
describe("User form async tests", () => {
it("Renders widget", async (done) => {
const promiseRole = new Promise((resolve, reject) =>
┊ setTimeout(
┊ ┊ () =>
┊ ┊ ┊ resolve({
┊ ┊ ┊ ┊ data: [
┊ ┊ ┊ ┊ ┊ { id: 0, name: "Admin" },
┊ ┊ ┊ ┊ ┊ { id: 1, name: "Editor" },
┊ ┊ ┊ ┊ ],
┊ ┊ ┊ }),
┊ ┊ 100
┊ )
);
const promiseLocs = new Promise((resolve, reject) =>
┊ setTimeout(
┊ ┊ () =>
┊ ┊ ┊ resolve({
┊ ┊ ┊ ┊ data: [
┊ ┊ ┊ ┊ ┊ { id: 0, name: "Loc1" },
┊ ┊ ┊ ┊ ┊ { id: 1, name: "Loc2" },
┊ ┊ ┊ ┊ ],
┊ ┊ ┊ }),
┊ ┊ 100
┊ )
);
┊ axios.get.mockImplementationOnce(() => promiseRole);
┊ axios.get.mockImplementationOnce(() => promiseLocs);
┊ const wrapper = mount(<UserForm />);
┊ await promiseRole;
┊ await promiseLocs;
┊ setImmediate(() => {
┊ ┊ wrapper.update();
┊ ┊ expect(wrapper.contains(Widget)).toEqual(true);
┊ ┊ done();
┊ });
});
});
Somehow it always returns false for the widget. After the data is fetched, what I did was I set the state to ready, thus it will render the Widget component. Interestingly, if i changed the code to mockResolvedValue straightaway then it works. What I don't understand is why the code above does not work? Basically it resolve the same value as well... can anyone please help? I'm really confused to get my mind around this and it is really bothering me
If axios returns the correct data, there is a code that calls
setReady(true)
, thus it returns <Widget ... />
after
Thanks
Working test because I mocked every get request:
jest.mock("axios");
describe("User form async tests", () => {
it("Renders widget", async (done) => {
┊ const location = [{ id: 0, name: "hurstville" }];
┊ const resp = { data: location };
┊ axios.get.mockResolvedValue(resp);
┊ const wrapper = mount(<UserForm />);
┊ setImmediate(() => {
┊ ┊ wrapper.update();
┊ ┊ expect(wrapper.contains(Widget)).toEqual(true);
┊ ┊ done();
┊ });
});
});
In UserForm
component I'm using formik
and material-ui
. So the problem lies at the getData()
function in useEffect
where it expect data type of
[{id: PropTypes.number, name: PropTypes.string}]
to be provided in <FormikSelect />
component for selection.
Thus the second working test works
UserForm:
import React, { useEffect, useState } from "react";
import {
FormikTextField,
FormikArray,
FormikSelect,
} from "app/components/forms/Utils";
import { FormikTransferList, Widget, Alert } from "app/components";
import { Formik, Form, ErrorMessage } from "formik";
import {
getUserById,
getUserRole,
addUser,
getLocationList,
updateUser,
} from "app/crud/user.crud";
import {
formValues,
validationUser,
} from "app/components/forms/users/UserConst";
import { useLocation, useHistory } from "react-router-dom";
import FuseSplashScreen from "@fuse/core/FuseSplashScreen";
import { Snackbar, Button } from "@material-ui/core";
const processedLocs = (data) =>
data.map((item) => {
return {
...item,
key: item.id,
title: item.name,
};
});
function UserForm() {
const [initial, setFormValues] = useState(formValues);
const location = useLocation();
const history = useHistory();
const [roles, setRoles] = useState([]);
const [locationList, setLocationList] = useState([]);
const [ready, setReady] = useState(false);
const [open, setOpen] = useState(false);
useEffect(() => {
async function getData() {
try {
const response = await getUserRole();
const locresult = await getLocationList();
if (response) {
setRoles(response.data);
setLocationList(processedLocs(locresult.data));
setReady(true);
}
} catch (err) {
console.log(err.message);
}
}
getData();
}, [getUserRole]);
useEffect(() => {
async function getUserDetails(id) {
const response = await getUserById(id);
if (response) {
const newVal = {
...response.data,
roles: response.data.roles.map((item) => ({
...item,
locations: item.locations.map((loc) => loc.id),
})),
};
setFormValues(newVal);
} else {
setFormValues(formValues);
}
}
if (location.state && location.state.id) {
getUserDetails(location.state.id);
}
}, [location.state]);
const handleCloseAlert = (event, reason) => {
if (reason === "clickaway") {
return;
}
setOpen(false);
};
const handleSuccess = () => {
setOpen(true);
};
return ready ? (
<>
<Formik
enableReinitialize
initialValues={initial}
validationSchema={validationUser}
onSubmit={async (
values,
{ setSubmitting, setErrors, setStatus, resetForm }
) => {
setTimeout(async () => {
try {
let response;
if (location.state && location.state.id) {
response = await updateUser(values.id, values);
} else {
response = await addUser(values);
if (response) {
handleSuccess();
}
}
resetForm(formValues);
history.goBack();
setStatus({ success: true });
} catch (err) {
setSubmitting(false);
setErrors({ submit: err.message });
}
}, 200);
}}
>
<Form>
<div className="container grid grid-cols-1 gap-4 p-8">
<Widget title="User Details" containerClass="p-8">
<div className="grid grid grid-cols-1 gap-4">
<FormikTextField
id="name"
variant="outlined"
name="name"
label="Full Name"
/>
<FormikTextField
id="email"
variant="outlined"
name="email"
label="Email Address"
/>
<FormikTextField
id="phone"
variant="outlined"
name="phone"
label="Phone number"
/>
</div>
</Widget>
<Widget title="User Roles" containerClass="p-8">
<div className="flex flex-col p-8">
<ErrorMessage
name="roles"
render={(msg) => (
<div className="text-lg text-red-600 p-8 ml-8">{msg}</div>
)}
/>
<FormikArray name="roles" defaultValue={initial.roles[0]}>
{({ index }) => (
<div className="flex flex-col p-8 mb-8">
<div>
<p className="text-lg font-semibold text-blue-400">
Assign locations to the role of the user. Each user
can has multiple locations based on the role that they
have
</p>
</div>
<div>
<p className="text-lg font-medium">Role {index + 1} </p>
</div>
<FormikSelect
labelId={`rolesSelectLabel${index}`}
options={roles}
label="Select Roles"
selectId={`rolesSelect${index}`}
name={`roles.${index}.id`}
/>
<div>
<p className="text-lg font-medium">Put Location:</p>
</div>
<div className="w-full">
<FormikTransferList
options={locationList}
name={`roles.${index}.locations`}
/>
</div>
</div>
)}
</FormikArray>
</div>
<div className="w-1/2 p-8">
<Button variant="contained" color="primary" type="submit">
{location.state && location.state.id ? "Update" : "Submit"}
</Button>
</div>
</Widget>
</div>
</Form>
</Formik>
<Snackbar open={open} autoHideDuration={4000} onClose={handleCloseAlert}>
<Alert onClose={handleCloseAlert} severity="success">
User Created
</Alert>
</Snackbar>
</>
) : (
<FuseSplashScreen />
);
}
UserForm.propTypes = {};
export default UserForm;