jest: rejects.toThrowError
jest version: 20.0.3
For sync method, it works in this way.
test('example', () => {
function fn() {
throw new Error('some error');
}
expect(fn).toThrowError('some error');
});
toThrowError
doesn’t work with promises.
test('example', async () => {
async function fn() {
throw new Error('some error');
}
await expect(fn()).rejects.toThrowError('some error');
});
I am getting an error:
expect(function).toThrowError(string)
Received value must be a function, but instead "object" was found
This works! The async function throws a function that throws an error.
test('example', async () => {
async function fn() {
const errorFunction = () => {
throw new Error('some error');
};
throw errorFunction;
}
await expect(fn()).rejects.toThrowError('some error');
});
Obiously it doesn’t make sense. I can see this line in the source code.
https://github.com/facebook/jest/pull/3068/files#diff-5d7e608b44e8080e7491366117a2025fR25
It should be Promise.reject(new Error())
instead of Promise.reject(() => {throw new Error();}
The example from the docs also doesn’t work
test('fetchData() rejects to be error', async () => {
const drinkOctopus = new Promise(() => {
throw new Error('yuck, octopus flavor');
});
await expect(drinkOctopus).rejects.toMatch('octopus');
});
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Reactions: 35
- Comments: 54 (15 by maintainers)
Commits related to this issue
- Updated tests to fix https://github.com/facebook/jest/issues/3601 — committed to franciscop/server by franciscop 7 years ago
- fix .toThrow for promises (#4884) * fix .toThrow for promises fixes #3601 Signed-off-by: Łukasz Sentkiewicz <sirmims@gmail.com> * keep original matcherName Signed-off-by: Łukasz Sentkiew... — committed to jestjs/jest by lstkz 7 years ago
- [test] Fix unhandled promise and the assertion. * Need to await * Had to use this way to assert, as suggested here: https://github.com/facebook/jest/issues/3601#issuecomment-333094824 — committed to artsy/metaphysics by alloy 6 years ago
- Updated tests to fix https://github.com/facebook/jest/issues/3601 — committed to williamProDev/simple-nodejs-server by williamProDev 7 years ago
- Updated tests to fix https://github.com/facebook/jest/issues/3601 — committed to topsmart0201/node-server by topsmart0201 7 years ago
+1 on this.
I use this for now:
This is a temporary workaround if you are willing to match the whole error message. Obviously, jest should support such basic thing.
Im confused - why does the documentation (still!) contain an example that doesnt work (https://facebook.github.io/jest/docs/en/expect.html#rejects) and prusumedly never worked?
Also why not solve it by having expect().toThrow() support promises? Then you could write:
expect(Promise.rejects(new Error('Some error'))).toThrow('Some error');
Mmm I see what you mean. Yet Jest’s documentation use the example of a real error with toMatch 😃 see https://facebook.github.io/jest/docs/expect.html#rejects . Either the example in the doc is wrong, or the implementation is wrong 😃
I think this is needlessly difficult to check an error’s message with Jest, especially when we want to match.
For anyone having trouble parsing the content of this thread, the following is how you do it:
You can do
expect(Promise.reject(new Error('something'))).rejects.toHaveProperty('message', 'something else');
which gives:Found it (I stumbled upon your issue because I had the same problem 😃)
Test passes on my machine. I think you can close it now.
@imeruli
reject
should be called with an Error object, not a literal string because you don’t get the stacktrace. It’s definitely a bad practice.When using async/await you should write
not
For anyone looking for a simple, working example of the problem posed in the original post:
Here is my solution:
It is essentially what @lachlanhunt posted.
If you simply execute the function under test with the arguments necessary to output the expected error, you can observe it in the catch block and do a simple string check.
This documentation example is still misleading (as mentioned above in 2017):
If
fetchData()
throws an Error, thetoMatch
fails because it expects a string:The example only works if the function throws a string instead of an Error, which is not a great assumption for the documentation to make.
What works is to replace
toMatch
withtoThrow
as mentioned above:Try awaiting inside the
expect
block, as inexpect(await drinkOctopus).rejects.toMatch('octopus');
What we want is to match a error message (that is, the
Error.prototype.message
property), just like the documentation says.Otherwise it’s easy to use something like
.toEqual(expect.any(Error))
To test multiple properties, the following is working:
@lachlanhunt that would work, of course, but it is extremely verbose compared to other jest workflows. Probably you also want to check before hand that err is actually defined (there was an error thrown), otherwise it’ll give an unhelpful undefined error.
BTW, for anyone reading so far, @ecstasy2 solution stopped working on Jest 21.0: https://github.com/facebook/jest/issues/4532
toMatch is what I want, the point of this bug is that it doesn’t work 😃 See last example in the initial comment.
@robertpenner your final example only tests whether an
Error
was thrown, but will match regardless of whether that error’s message wassome error
or not:I believe the
.toHaveProperty
or.toMatchObject
approaches are the best for this situation.Here’s how I’m dealing with this in the event that I want to assert on properties of the error:
@agm1984 your test will pass if
addItem
does not throw, unless you useexpect.assertions
: DocumentationAs a workaround for this issue, I came up with this aproach:
If it doesn’t throw,
err
will be undefined and fail to read themessage
property. Otherwise, if it does throw, it verifies the message matches.@jcollum the catch is needed because you are configuring a test dependency to return a rejected promise which causes your function under test to reject (1). You don’t need the done and you can do
rejects.toThrow()
you just have to return the expectation and assert on it (2). Also controlling what something returns (1) and asserting it was called is probably unnecessary (deleted in my example).For the async awaiters around these parts you can achieve the same thing you just don’t want the async function of the test to return a rejected promise or else jest will fail the test (1). We want to catch that it would fail and do an assertion that way the test still passes (2). When catching you are handling the error and a non reject promise is returned
As explained and based on the work here, I made a small function to help:
It has the added benefit compared to
rejects
that if you forget either theawait
or thesync
, it’ll throw errors and not fail silently. Same as if you try to pass a non-promise.Is this considered a bug or just the way Jest is supposed to work? If the latter is true, the documentation should reflect this. Currently it says you can use:
Which doesn’t actually work if
drinkOctopus
is an instance of anError
, which as discussed earlier is considered “best practice”. ThetoMatchObject
solution mentioned previously doesn’t work as of Jest 21, so you are reduced to things liketoHaveProperty
, which work, but doesn’t support partial matching.This feels cumbersome, to me. Is there a planned way to handle these rejections?
+1 same here. just spent an hour trying to work our why I cant use expect().toThrow() when testing (async) mongoose DB actions & validators (with a not-very-useful jest message “Received value must be a function, but instead “object” was found”)
What about a
toRejectWith()
that handles that message? This being a specific situation for rejected errors:Or even this:
Isentkiewicz, I do no not disagree, however, from a specification standpoint, it is written nowhere.