jest: Make console.error (and warn?) log a stack trace and codeframe like thrown errors do
š Feature Proposal
99% of the time, if console.error
and console.warn
are called during tests (and theyāre not mocked), itās something that needs to be fixed.
Motivation
In particular, with Reactās new act
warning, people are seeing output like this:
console.error node_modules/react-dom/cjs/react-dom.development.js:545
Warning: An update to User inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser. Learn more at https://fb.me/react-wrap-tests-with-act
in User
Itās not entirely helpful. It does show that this is happening in the User
component, but there could be any number of places within that component that could have triggered that error to be logged.
Example
Hereās a proof of concept of what Iām suggesting (definitely wouldnāt work like this if built-in):
const originalError = console.error
beforeAll(() => {
console.error = (...args) => {
console.log(new Error().stack)
originalError(...args)
}
})
afterAll(() => {
console.error = originalError
})
This would look like:
console.log src/__tests__/tmp.js:31
Error:
at CustomConsole.console.error (/Users/kentcdodds/code/react-testing-library/src/__tests__/tmp.js:31:17)
at warningWithoutStack (/Users/kentcdodds/code/react-testing-library/node_modules/react-dom/cjs/react-dom.development.js:545:32)
at warnIfNotCurrentlyActingUpdatesInDEV (/Users/kentcdodds/code/react-testing-library/node_modules/react-dom/cjs/react-dom.development.js:23281:7)
at dispatchAction (/Users/kentcdodds/code/react-testing-library/node_modules/react-dom/cjs/react-dom.development.js:15813:9)
at setUser (/Users/kentcdodds/code/react-testing-library/src/__tests__/tmp.js:8:5)
at processTicksAndRejections (internal/process/task_queues.js:89:5)
console.error src/__tests__/tmp.js:32
Warning: An update to User inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser. Learn more at https://fb.me/react-wrap-tests-with-act
in User
And visually:
And it could be potentially improved within Jest so itās even more helpful (with color coding to highlight the part of the code that triggered the error). Similar to when an error is thrown.
some error in React
6 | async function fetchUserData(id) {
7 | const response = await fetch(`/${id}`)
> 8 | setUser(await response.json())
| ^
9 | }
10 | React.useEffect(() => {
11 | fetchUserData(props.id)
at dispatchAction (node_modules/react-dom/cjs/react-dom.development.js:15809:11)
at setUser (src/__tests__/tmp.js:8:5)
And visually:
To be clear, what I mean is that console.error
could print out a stack trace and codeframe where the part of the stack trace which is not in user code could be dimmed to reduce confusion and the codeframe would show the part of the userās code which ultimately triggered the error to be logged.
Pitch
I think this would help more than just act
and it seems like the correct place for this to happen. It would help people get rid of error (and potentially warn) logs quicker, and move on to building their apps faster (which I believe is the goal of Jest).
Reference: https://github.com/testing-library/react-testing-library/issues/434
Iād love to hear what you all think!
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 59
- Comments: 39 (26 by maintainers)
Doing this only for
console.error
andconsole.warn
matches how browsers show expandable arrows with JS stack next to them. The implementation itself is justnew Error().stack
. Sure, itās not asynchronous ā but thatās a question to VM implementors. Eventually it would be (or maybe it already is in recent Nodes). This sounds like a simple enough proposal and likely doable in 30 minutes. Let me tweet this.Released in 25.4.0, please try it out š
Since this is targeting Node, I would recommend
Error.captureStackTrace
with theconstructorOpt
parameter, which can be used to avoid adding unnecessary Jest-internal frames to the stack.Simplified example:
(EDIT: Fixed the example.)
I believe Jest filters stack traces anyway before displaying them, but itās probably better to avoid adding unnecessary info than rely on filtering to remove it.
Next release will respect
noStackTrace
I agree it makes sense, but itās also technically a breaking change. We could duplicate it over though, and only break it in v26
I think it would be a mistake to do it for all console methods š¬ like @FredyC said, console.log is a common workflow method people engage in and it could get very noisy very quickly. Itās unlikely that if thereās a console.log I donāt know what called it. The benefit of console.error/warn is that they typically exist in dependency code and are unexpected so require some debugging to determine the root cause.
This is how React does it https://github.com/facebook/react/blob/master/scripts/jest/setupTests.js#L69-L128 Works well for us
One challenge I noticed is that
jest-message-util
depends onjest-test-result
, andjest-test-result
depends onjest-console
. Ifjest-console
tries to usejest-message-util
, it creates a circular dependency.jest-message-util
depends onjest-test-result
because of the following function: https://github.com/facebook/jest/blob/db055c2f413b1301d69c5a8924ba811bdd7c0e53/packages/jest-message-util/src/index.ts#L288Since there are a number of test result formatters in
jest-test-result
, does it make sense to moveformatResultError
tojest-test-result
? That way we can make bothjest-test-result
andjest-console
depend onjest-message-util
.We have a util for formatting errors that attach a code frame already, I think it makes sense to reuse that: https://github.com/facebook/jest/blob/677b763ec0a8c592c7395901c8efa1491c138c61/packages/jest-message-util/src/index.ts#L248
captureStackTrace
which is used here and there throughout the code base: https://github.com/facebook/jest/blob/677b763ec0a8c592c7395901c8efa1491c138c61/packages/jest-util/src/ErrorWithStack.ts@ParthS007 Just a heads up this might not be necessarily the most beginner-friendly issue. Please feel free to dig into it but I think we shouldnāt hold off progress on fixing this if someone else wants to work on it too.
I had entered https://github.com/facebook/jest/issues/8819 For the Winston logging issue, after going through above conversation i want to make sure that our workflow is not impacted:
`PASS test/ui/admin/ahanges.parallel.test.js (25.328s) ā Console
b) Logs getting interleaved (Undesirable for us and itās good that jest currently doesnāt do this): Logs show up in near realtime but they are interleaved (with jest workerID for mutliple tests running in parallel showing up) as shown below. (I am running an older version of jest and older winston to exhibit this behavior) `2019-08-09 15:30:54 WorkerID:5 info: Login Page - Entered password
2019-08-09 15:30:55 WorkerID:3 info: Updating app session via API
2019-08-09 15:30:54 WorkerID:1 info: Login Page - Entered username:
2019-08-09 15:30:55 WorkerID:3 info: Navigating to: https://engineering-zzz
2019-08-09 15:30:55 WorkerID:1 info: Login Page - Entered username:`
I see above there was conversation around console.logs not being used much - we actually use it quite a bit along with console.warn and console.error as well. I want to make sure that with the changes agreed to in this ticket it doesnāt break jestās current behavior of consoleBuffering when multiple tests are run. Also, this feature is already used in jest-junit https://github.com/jest-community/jest-junit/issues/45 so another reason for not breaking this feature. So essentially current jest behavior as described in 2 a) is preserved.
Also, I agree that this should be automatic and opt out with
--noStackTrace
as @jeysal suggested.I would argue itās needed for all console types. From practice, the
console.log
is often used for debugging tests when I am lazy enough to start a debugger. I might have 5 or more log statements at the time and if each of them would spill out a full stack trace, it would pretty awful to find anything useful there.Unless of course some sort of filtering would be applied. I think it makes total sense to start at first frame that is out of
node_modules
and take like 3-5 following ones. Thatās usually enough to pinpoint the origin.Or make it configurable for each log level, although people with CRA would be unable to change it anyway š¤·āā