Tape: Exceptions throw in `test('.', async (t) => { ... })` do not have meaningful stacktrace

Created on 30 Dec 2020  路  2Comments  路  Source: substack/tape

When using tape@5 instead of tape@4 a programmer error in my test program is captured by tape and showing a not ok ${err.message}

# sync
not ok 1 TypeError: Cannot read property 'length' of undefined
  ---
    operator: fail
    stack: |-
      Error: TypeError: Cannot read property 'length' of undefined
          at Test.assert [as _assert] (/tape/lib/test.js:260:54)
          at Test.bound [as _assert] (tape/lib/test.js:84:32)
          at Test.fail (tape/lib/test.js:354:10)
          at Test.bound [as fail] (tape/lib/test.js:84:32)
          at onError (tape/lib/test.js:114:18)
  ...

The stack trace is the stack trace of tape itself handling promise.catch() ( https://github.com/substack/tape/blob/master/lib/test.js#L113 ) instead of the stack trace for my code.

I cannot tell which line of code in my test function has .length of undefined because tape is printing the wrong stack trace.

Ideally tape would rethrow the err to the global process uncaught exception handler so that I can see the real stack trace and so that the tests can fail hard and fast immediately.

Most helpful comment

A meaningful stack trace is important.

However, node 15's default behavior of crashing on unhandled rejections violates language semantics - an unhandled rejection is not the same as an uncaught exception, and it's not supposed to fail the program overall, fast or otherwise.

It seems like the real issue is that https://github.com/substack/tape/blob/master/lib/test.js#L260 is creating a new Error, and thus a new stack trace, rather than preserving the original error instance. I think it should be doable to fix it so that either 1) when fail is called with an error instance, the error instance is preserved, or 2) specifically in the case of a rejected promise returned to tape, we directly use _assert or .emit rather than passing it through .fail.

All 2 comments

A meaningful stack trace is important.

However, node 15's default behavior of crashing on unhandled rejections violates language semantics - an unhandled rejection is not the same as an uncaught exception, and it's not supposed to fail the program overall, fast or otherwise.

It seems like the real issue is that https://github.com/substack/tape/blob/master/lib/test.js#L260 is creating a new Error, and thus a new stack trace, rather than preserving the original error instance. I think it should be doable to fix it so that either 1) when fail is called with an error instance, the error instance is preserved, or 2) specifically in the case of a rejected promise returned to tape, we directly use _assert or .emit rather than passing it through .fail.

Yeah I think calling self.ifError(err) instead of self.fail(err) should preserve the stack trace.

However we should probably double check the rejection is an Error instance and call self.fail(reason) with any non-Error reason.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vasiliicuhar picture vasiliicuhar  路  7Comments

frankandrobot picture frankandrobot  路  4Comments

shaunmcarmody picture shaunmcarmody  路  7Comments

AndyOGo picture AndyOGo  路  8Comments

dcousens picture dcousens  路  3Comments