Context: React application with mobx.
Anyway I've a class (a store), catalogStore, with a loadProducts method. This method call a service to get the data and then, return it.
I've to write a test that say "If it cannot get the data, then throw an exception"
I mocked the function supposed to get the data, forcing it to reject... ok
This is the test I wrote
describe("catalogStore", () => {
describe("if the catalog fails to get the data", () => {
beforeAll(() => {
catalogService.get = jest.fn().mockImplementation(() => {
return new Promise((resolve, reject) => {
reject("rejected error");
});
});
});
it("should throw an error", () => {
return expect(() => catalogStore.loadProducts()).toThrow();
});
});
});
And this is the loadProducts function:
loadProducts() {
return catalogService
.get()
.then(result => {
this.products = result.services;
return {products: this.products};
})
.catch(error => {
console.log("CatalogStore loadProducts error catch: ", error);
return { error };
})
.then(({ error }) => {
if (error) {
console.log("Im gonna throw the error -> ", error);
throw error;
}
});
}
From the logs I can see "Im gonna throw the error -> rejected error", but the test fails with this message:
Expected the function to throw an error. But it didn't throw anything.
Why? I'm throwing the error.
Luca
Your error is thrown in the context of a Promise chain callback. It will be caught by the Promise and passed to the next catch handler.
To modify your test to inspect the error you could use Jest's Promise expectations:
describe("catalogStore", () => {
describe("if the catalog fails to get the data", () => {
beforeAll(() => {
catalogService.get = jest.fn().mockImplementation(() => {
return new Promise((resolve, reject) => {
reject("rejected error");
});
});
});
it("should throw an error", () => {
return expect(catalogStore.loadProducts()).rejects.toThrow('rejected error');
});
});
});
Its cause the function returns a promise, so all jest see is that the get() function is the called but as the error happens in a promise the test is finished before the error is thrown. To test that promises have look on how async error handling works.
Main idea is that you have an async function where you catch the failing promises by yourself
it('fails', async()=>{
try{
await catalogStore.loadProducts()
} catch(e) {
expect(e).toBeDefined()
}
})
Related
I have a GET Axios request in which I pass a github user and get information about him. I need to process the option when the request gives an error, which I process in catch (). How can i do this?
//get user
async componentDidMount() {
getUser(this.props.user)
.then((response) => {
this.setState(response.data);
})
.catch((error) => {
this.setState({
error: "request error"
});
});
}
//jest
jest.mock("axios");
describe("UserList Component", () => {
it("should return user login", async () => {
const user = { data: "login1" };
axios.get.mockResolvedValueOnce(user);
const res = await getUser(user);
expect(res.data).toContain("login1");
});
it("should return error", async () => {
//Here I need to write a test in which I check the error so that the error text matches
});
this is the answer testing the catch block using jest
expect(() => {
const model = new Sample(resolvedSample)
}).toThrow(TypeError);
I wanted to run different error handling with React Promise.
API 1 and 2 should have different error handlings.
Execute APIs all at once to save time.
Run different error handling statements for each API as soon as possible, without waiting for the others.
Each API should continue even if one fails.
How can this be done?
Reference:
Fetch API requesting multiple get requests
Promise.all([
fetch(api1).then(value => value.json()),
fetch(api2).then(value => value.json())
])
.then((value) => {
console.log(value)
//json response
})
.catch((err) => {
console.log(err);
});
Promise.all is just wrapping up whatever promises you give it - so there's no reason you couldn't handle the errors separately for each one. For example, you could create a separate function for each of the fetches - you could even throw a custom error here that dictates some sort of "followUp" action to do, or identifies where the error is from, or anything (you can throw anything in javascript):
const fetchFromApi1 = async () => {
try {
const response = await fetch(api1);
return response.json();
} catch (err) {
console.log('API 1 failed');
// Throw a custom error
throw {
errorSource: 'API_CALL_1',
message: 'API call 1 failed',
};
}
};
const fetchFromApi2 = async () => {
// ----- 8< -----
};
Then you can just combine them in your your Promise.all - if you've thrown a custom error as above, you can use that to work out what to do:
const fetchAllTheThings = async () => {
try {
const [response1, response2] = await Promise.all([
fetchFromApi1(),
fetchFromApi2(),
]);
} catch (err) {
const { errorSource, message } = err;
// do something....
}
};
Edit
If you want to know which promise failed at the point of calling, you're probably better off using allSettled -
const fetchAllTheThings = async () => {
const [result1, result2] = await Promise.allSettled([
fetchFromApi1(),
fetchFromApi2(),
]);
if (result1.status === 'rejected') {
// Sad for promise 1
}
if (result2.status === 'rejected') {
// Sad for promise 2
}
};
const p1 = new Promise((res, rej) => {
setTimeout(() => {
res("p1 success")
}, 1000)
})
const p2 = new Promise((res, rej) => {
setTimeout(() => {
res("p2 success")
}, 3000)
})
const p3 = new Promise((res, rej) => {
setTimeout(() => {
rej("p3 failed")
}, 1000)
})
const p4 = new Promise((res, rej) => {
setTimeout(() => {
rej("p4 failed")
}, 2000)
})
Promise.allSettled([p1, p2, p3, p4])
.then(console.log)
I have a component that fetches data wrapped in a function to made async calls cancelable:
useEffect(() => {
const asyncRequest = makeCancelable(myService.asyncRequest());
asyncRequest.promise
.then((result) =>
setState(result),
)
.catch((e) => {
if (!e?.isCanceled) {
//Case the rejection is not caused by a cancel request
throw e;
}
});
return () => {
asyncRequest.cancel();
};
},[])
I want to test that, when the rejection is not coming from a cancel request, the error is re-thrown (I'm filtering out cancel rejections since they are not true errors). So the goal is intercept exceptions coming from useEffect
How can I test it with enzyme and/or jest?
it('should not filter rejection not caused by cancel', () => {
let promise = Promise.reject(new Error('Generic error'));
when(myService.asyncRequest()).thenReturn(promise); // This will cause useEffect to throw
const myComponent = mount(<MyComponent />) // How to intercept the error?
})
To give further context here is the code of makeCancelable:
export function makeCancelable<T>(promise: Promise<T>): CancelablePromise<T> {
let isCanceled = false;
const wrappedPromise = new Promise<T>((resolve, reject) => {
promise.then(
(val) => (isCanceled ? reject({ isCanceled: true }) : resolve(val)),
(error) => (isCanceled ? reject({ isCanceled: true }) : reject(error)),
);
});
return {
promise: wrappedPromise,
cancel() {
isCanceled = true;
},
};
}
Sorry if title was a bit unclear, what I want to do is catch the member.send, but I don't know how to use try and catch when also using timeinterval. It gives me an error saying that I haven't handled it.
message.guild.members.forEach(member => {
try {
setInterval(() => {
member.send("hello");
}, 2000)
}
catch(e) {
console.log("couldnt send dm to a user!");
}
Second problem: Cannot read property of 'guild' of undefined, and UnhandledPromiseRejection
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// create an async function and run it
(async function(message) {
for (const [id, member] of message.guild.members) {
try {
// await will throw any error found and allow try/catch to work
await member.send("hello");
} catch (err) {
console.log("Found error", err);
}
await sleep(2000);
}
})();
try/catch doesn't work for promise rejections, nor does it work when wrapped around a setInterval. Just catch the promise:
member.send("hello").catch(err => {
console.error(`Got error ${err}.`);
});
If you want to send a message to each person then the best way is to use a promise-based sleep function:
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// create an async function and run it
(async function() {
for (const [id, member] of message.guild.members) {
try {
// await will throw any error found and allow try/catch to work
await member.send("hello");
} catch (err) {
console.log("Found error", err);
}
await sleep(2000);
}
})();
My action is like this:-
export function requestModel(param) {
return (dispatch) => {
dispatch({
type: 'REQUEST',
loadModel: true,
});
dispatch(getModel(param)).then(response => response.json())
.then(model=>
dispatch(receiveModel(model)))
.catch(() => {
dispatch({
type: MODEL_FAILURE,
loadModel:false,
});
});
};
}
I wrote the test case to cover catch block as :-
it('requestModel() handle sxception...............', (done) => {
const response = { json: () => data };
// call the actual function with stubbed function
try {
dispatchStub.withArgs(getDeviceApiStub).returns(Promise.reject(response));
dispatchStub.withArgs(getDeviceActionStub).returns(Promise.resolve(response));
const returnFunction = modelAction.requestModel(actionParam);
returnFunction(dispatchStub);
done();
sinon.assert.calledWithMatch(dispatchStub, {
type: MODEL_FAILURE,
loadModel:false,
});
done();
} catch (err) {
done(err);
}
});
but the issue is that before catch block of method it is calling the sinon.assert as i wrote above. How to deal with this, i used async await also but same issue is coming, Is there any why so that I can write test cases for the catch block of my action in reactjs?