-
Notifications
You must be signed in to change notification settings - Fork 129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Testing error throw inside saga #147
Comments
I also want to test a saga that, in a couple very specific failure cases, throws an exception. I have the test running smoothly but it still logs the error (even in 3.2.0). Since run() correctly returns the rejected promise, I'm not sure there's a need to allow any error logging in that case. |
Hi @hidalgofdz! The current problem is in how |
Hey @jfairbank So I ran into an issue with testing error cases with
something like this, or exactly what So I guess its not quite the issue this person has, since it doesnt have anything to actually do with redux-saga, but its keeping us from testing error cases with So my questions:
This is for a very large company, where I'm starting to push for redux-saga adoption company wide, so we'd probably have an interest in helping you maintain |
@hedgerh I believe this is JavaScript functioning as intended actually. If you run this code in Node or a browser, you'll get an uncaught exception. function* saga() {}
saga().throw(new Error('whoops')) Jest or any other test framework should see that as any other uncaught exception and rightfully blow up. What error cases are you unable to test? I'm assuming you're using try-catch blocks for those cases in your saga? That being said, I wouldn't be opposed to maybe catching the errors in Thoughts? And of course, I'd definitely take any help your or your company could give. Most of my normal open source time has been consumed with other work that probably won't be finished until late winter or early spring 2018. |
I also came to this problem. In saga.docs/error-handling is this example // expects a dispatch instruction
assert.deepEqual(
iterator.throw(error).value,
put({ type: 'PRODUCTS_REQUEST_FAILED', error }),
"fetchProducts should yield an Effect put({ type: 'PRODUCTS_REQUEST_FAILED', error })"
) but if your generator is written like this (again, taken from docs) function* fetchProducts() {
try {
const products = yield call(Api.fetch, '/products')
yield put({ type: 'PRODUCTS_RECEIVED', products })
}
catch(error) {
yield put({ type: 'PRODUCTS_REQUEST_FAILED', error })
}
} then in Jest the You must give iterator chance to step into like this // void next() call just for moving to first yield
iterator.next()
// then the throw() is handled by iterators try { } block
assert.deepEqual(
iterator.throw(error).value,
put({ type: 'PRODUCTS_REQUEST_FAILED', error }),
"fetchProducts should yield an Effect put({ type: 'PRODUCTS_REQUEST_FAILED', error })"
) |
thank you so much @VClav. this was exactly the issue I was having. I was immediately calling throw instead of calling next first. sweet. I can use the lib now! |
@VClav, you win the gold medal. In my case, I was calling next, but not enough times to get into my try block. I've been searching for this answer for DAYS! Thanks for explaining why calling next was actually important. |
I too am trying to test a saga that I expect to throw an error. I can test this as follows: it('throws an error', done => (
expectSaga(mySaga)
.run()
.catch(e => {
expect(e).toBeInstanceOf(ErrorResponse);
done();
})
)); However I still end up with the error output in my console as described by @hidalgofdz. Which means that the error logging could be disabled by adding a |
In Jest, this works for me, nothing too special, see the function in expect()
|
@dabrowne did you find any way to omit the errors on your console when your saga throws an error? |
Hi everyone, Thanks for the work on #211 and #217 However, I don't understand how does #211 actually helps in suppressing the error message logged in the console. Here is a sample code showing I'm not able to make the console error go away. Production code : export function* doSomethingSaga() {
const iCanDo = yield call(isThisAuthorized);
if (!iCanDo)
throw new Error("You're not authorized to do something");
} Testing code : it("reports if user is not authorized", () => {
return expectSaga(doSomethingSaga)
.provide([[call(isThisAuthorized), false]])
.throws(new Error("You're not authorized to do something"))
.run();
}); Please note that the above test passes : it's GREEN, but it produces the following output in console : As you can see, I still see the Error's message in the console output. Exact stack trace
I'm using :
Am I missing something ? |
@CadiChris I ran into the same problem. Here is my current workaround expectSaga(function*() {
try {
yield call(getUser, accessToken);
} catch (error) {
expect(error).toEqual(new Error('Access token is not valid anymore'));
}
})
.provide([
[call(getApiConfig), apiConfig],
[matchers.call.fn(post), { data: {} }],
])
.run(); |
In combination with jest I can do something along the lines: // saga
export function* demo() {
throw new Error('Some Error);
}
// test
it('should throw an error', async () => {
await expect(
expectSaga(demo)
.dispatch({ type: DEMO })
.silentRun()
).rejects.toThrowError('Some Error');
}); |
Thanks for your answers ! @euphocat That's clever ! With your approach I don't see the error in the console anymore. 🎉 function forExceptionTest(saga, ...sagaArgs) {
return {
assertThrow: expectedError => {
let errorWasThrown = false;
return function*() {
try {
yield call(saga, ...sagaArgs);
} catch (e) {
errorWasThrown = true;
expect(e).toEqual(expectedError);
} finally {
if (!errorWasThrown)
throw "Error was expected, but was not thrown by saga";
}
};
},
};
}; The test will also fail if error is not thrown at all. And I used it like so : it("reports if user is not authorized", async () => {
await expectSaga(
forExceptionTest(doSomethingSaga).assertThrow(
new Error("You're not authorized to do something")
)
)
.provide([[call(isThisAuthorized), false]])
.run();
}); |
I managed to get it working for my use case, using this approach.
|
you can also use this |
This is my solution # code
* SSO (ssoType) {
let payload = null;
switch (ssoType) {
case 'google':
payload = yield call(google.login);
break;
case 'facebook':
payload = yield call(facebook.login);
break;
default:
throw Error(`${ssoType} does not support`);
}
},
# test
expect(
() => SSO('xxx').next()
).toThrowError(new Error(`${$ssoType} does not support`));
# or
try {
SSO('xxx').next()
} catch (error) {
expect(new Error(`${$ssoType} does not support`));
}
# testsaga way
expect(
() => testSaga(SSO, 'xxx').next()
).toThrowError(new Error(`${$ssoType} does not support`));
|
The fix made in this pull request is no longer valid with According to this doc https://redux-saga.js.org/docs/api/#runsagaoptions-saga-args, logger is not supported anymore. A simple solution could be by setting |
My workaround:
|
Is there a recommended way to test sagas that throw errors? I'm trying to create a test for this function:
The only way I could find to test the function above is using something like this:
The problem is that even when the test passes a console.error is throw:
I don't know if the problem is the function, the test, or both. Is throwing errors inside sagas a good practice? I would really appreciate any feedback.
The text was updated successfully, but these errors were encountered: