Skip to content

Normalize .rejects.toThrow#16163

Open
joshkel wants to merge 1 commit into
jestjs:mainfrom
joshkel:rejects-tothrow
Open

Normalize .rejects.toThrow#16163
joshkel wants to merge 1 commit into
jestjs:mainfrom
joshkel:rejects-tothrow

Conversation

@joshkel
Copy link
Copy Markdown
Contributor

@joshkel joshkel commented May 11, 2026

Summary

.rejects.toThrow is inconsistent with .toThrow: .toThrow will accept any string or anything with a message, while .rejects.toThrow only accepts Error objects.

Besides the general inconsistency, this causes a couple of specific issues:

This has been reported a few times: #12024, #9748, #6675. See also the original implementation at #5670.

Specific notes:

  • The linked GitHub issues discuss whether "throws" is even the appropriate term for promises. Intuitively, I would expect "rejects" to be equivalent to "throws from an async function," so I would expect Jest's .rejects.toThrow to be implemented accordingly.
  • As a result of Jest's previous implementation, code like expect(Promise.resolve(new Error('apple'))).resolves.toThrow('apple') passes. This does not match my intuition, but I'm preserving it for backward compatibility.
  • Previous versions of Jest unwrap functions within promises: expect(Promise.reject(() => { throw new Error('apple'); })).rejects.toThrow('apple') passes. This is unusual, but I'm preserving it for backward compatibility.

Test plan

yarn test

Note: I have not yet updated the changelog, because I'm not certain if this should be classified as a feature or a fix.

`.rejects.toThrow` is inconsistent with `.toThrow`: `.toThrow` will accept any string or anything with a `message`, while `.rejects.toThrow` only accepts Error objects.

Besides the general inconsistency, this causes a couple of specific issues:

* It fails with `Error` objects from other realms (which can happen to with native modules across Jest's vm realms). (See https://github.com/jestjs/jest/blob/v30.4.2/packages/expect-utils/src/utils.ts#L559 and [`Error.isError`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/isError).)
* Because of the toThrowMatchers implementation, if a non-Error is rejected, it will be treated as if nothing was thrown, resulting in a very confusing error message ("Received function did not throw").

This has been reported a few times: jestjs#12024, jestjs#9748, jestjs#6675. See also the original implementation at jestjs#5670.

Specific notes:

- The linked GitHub issues discuss whether "throws" is even the appropriate term for promises. Intuitively, I would expect "rejects" to be equivalent to "throws from an async function," so I would expect Jest's `.rejects.toThrow` to be implemented accordingly.
- As a result of Jest's previous implementation, code like `expect(Promise.resolve(new Error('apple'))).rejects.toThrow('apple')` passes. This does not match my intuition, but I'm preserving it for backward compatibility.
- Previous versions of Jest unwrap functions within promises: `expect(Promise.reject(() => { throw new Error('apple'); })).rejects.toThrow('apple')` passes. This is unusual, but I'm preserving it for backward compatibility.
@netlify
Copy link
Copy Markdown

netlify Bot commented May 11, 2026

Deploy Preview for jestjs ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit 6d9896f
🔍 Latest deploy log https://app.netlify.com/projects/jestjs/deploys/6a02453ce28716000842094c
😎 Deploy Preview https://deploy-preview-16163--jestjs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 11, 2026

Open in StackBlitz

babel-jest

npm i https://pkg.pr.new/babel-jest@16163

babel-plugin-jest-hoist

npm i https://pkg.pr.new/babel-plugin-jest-hoist@16163

babel-preset-jest

npm i https://pkg.pr.new/babel-preset-jest@16163

create-jest

npm i https://pkg.pr.new/create-jest@16163

@jest/diff-sequences

npm i https://pkg.pr.new/@jest/diff-sequences@16163

expect

npm i https://pkg.pr.new/expect@16163

@jest/expect-utils

npm i https://pkg.pr.new/@jest/expect-utils@16163

jest

npm i https://pkg.pr.new/jest@16163

jest-changed-files

npm i https://pkg.pr.new/jest-changed-files@16163

jest-circus

npm i https://pkg.pr.new/jest-circus@16163

jest-cli

npm i https://pkg.pr.new/jest-cli@16163

jest-config

npm i https://pkg.pr.new/jest-config@16163

@jest/console

npm i https://pkg.pr.new/@jest/console@16163

@jest/core

npm i https://pkg.pr.new/@jest/core@16163

@jest/create-cache-key-function

npm i https://pkg.pr.new/@jest/create-cache-key-function@16163

jest-diff

npm i https://pkg.pr.new/jest-diff@16163

jest-docblock

npm i https://pkg.pr.new/jest-docblock@16163

jest-each

npm i https://pkg.pr.new/jest-each@16163

@jest/environment

npm i https://pkg.pr.new/@jest/environment@16163

jest-environment-jsdom

npm i https://pkg.pr.new/jest-environment-jsdom@16163

@jest/environment-jsdom-abstract

npm i https://pkg.pr.new/@jest/environment-jsdom-abstract@16163

jest-environment-node

npm i https://pkg.pr.new/jest-environment-node@16163

@jest/expect

npm i https://pkg.pr.new/@jest/expect@16163

@jest/fake-timers

npm i https://pkg.pr.new/@jest/fake-timers@16163

@jest/get-type

npm i https://pkg.pr.new/@jest/get-type@16163

@jest/globals

npm i https://pkg.pr.new/@jest/globals@16163

jest-haste-map

npm i https://pkg.pr.new/jest-haste-map@16163

jest-jasmine2

npm i https://pkg.pr.new/jest-jasmine2@16163

jest-leak-detector

npm i https://pkg.pr.new/jest-leak-detector@16163

jest-matcher-utils

npm i https://pkg.pr.new/jest-matcher-utils@16163

jest-message-util

npm i https://pkg.pr.new/jest-message-util@16163

jest-mock

npm i https://pkg.pr.new/jest-mock@16163

@jest/pattern

npm i https://pkg.pr.new/@jest/pattern@16163

jest-phabricator

npm i https://pkg.pr.new/jest-phabricator@16163

jest-regex-util

npm i https://pkg.pr.new/jest-regex-util@16163

@jest/reporters

npm i https://pkg.pr.new/@jest/reporters@16163

jest-resolve

npm i https://pkg.pr.new/jest-resolve@16163

jest-resolve-dependencies

npm i https://pkg.pr.new/jest-resolve-dependencies@16163

jest-runner

npm i https://pkg.pr.new/jest-runner@16163

jest-runtime

npm i https://pkg.pr.new/jest-runtime@16163

@jest/schemas

npm i https://pkg.pr.new/@jest/schemas@16163

jest-snapshot

npm i https://pkg.pr.new/jest-snapshot@16163

@jest/snapshot-utils

npm i https://pkg.pr.new/@jest/snapshot-utils@16163

@jest/source-map

npm i https://pkg.pr.new/@jest/source-map@16163

@jest/test-result

npm i https://pkg.pr.new/@jest/test-result@16163

@jest/test-sequencer

npm i https://pkg.pr.new/@jest/test-sequencer@16163

@jest/transform

npm i https://pkg.pr.new/@jest/transform@16163

@jest/types

npm i https://pkg.pr.new/@jest/types@16163

jest-util

npm i https://pkg.pr.new/jest-util@16163

jest-validate

npm i https://pkg.pr.new/jest-validate@16163

jest-watcher

npm i https://pkg.pr.new/jest-watcher@16163

jest-worker

npm i https://pkg.pr.new/jest-worker@16163

pretty-format

npm i https://pkg.pr.new/pretty-format@16163

commit: 6d9896f

@SimenB
Copy link
Copy Markdown
Member

SimenB commented May 12, 2026

I like this, thanks! However, I wonder if it's a breaking change and should wait for Jest 31? Any code relying on .rejects.not.toThrow() passing when the rejection value is a non-Error will now fail.

@joshkel
Copy link
Copy Markdown
Contributor Author

joshkel commented May 12, 2026

Yes, it would be a breaking change.

I can try covering the special case of .rejects.not.toThrow() if you like (requiring that it be an Error if no parameters are supplied), but even then there are potential behavior changes. It might be cleaner to wait and treat as a breaking change.

Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants