Give a boost to check error messages of your abstractions

Give a boost to check error messages of your abstractions

[ad_1]

Shall we embrace you have got this check:

const upload = (a, b) => a + b

if (upload(1, 2) !== 4) {
  throw new Error('Anticipated 3 to be 4')
}

(Sure, that may be a check).

When you run that with node, this is the output that you must be expecting:

upload.check.js:4
  throw new Error('Anticipated 3 to be 4')
  ^

Error: Anticipated 3 to be 4
    at upload.check.js:4:9
    at Script.runInThisContext (vm.js:116:20)
    at Object.runInThisContext (vm.js:306:38)
    at Object.<nameless> ([stdin]-wrapper:9:26)
    at Module._compile (inside/modules/cjs/loader.js:959:30)
    at evalScript (inside/procedure/execution.js:80:25)
    at inside/primary/eval_stdin.js:29:5
    at Socket.<nameless> (inside/procedure/execution.js:192:5)
    at Socket.emit (occasions.js:215:7)
    at endReadableNT (_stream_readable.js:1184:12)

That is an attractive usual stack hint for that error. The message is clear-ish,
however we will do higher and we do! If we write this identical check with Jest, the
ensuing error is a lot more useful:

check('sums numbers', () => {
  be expecting(upload(1, 2)).toBe(4)
})

That may fail with an error like this:

FAIL  ./upload.check.js
sums numbers (3 ms)

sums numbers

    be expecting(won).toBe(anticipated) // Object.is equality

    Anticipated: 4
    Won: 3

      2 |
      3 | check('sums numbers', () =>                      ^
      5 )
      6 |

      at Object.<nameless> (src/__tests__/upload.js:4:21)

It seems to be even higher within the terminal:

visual output of the above

Great proper? Particularly that codeframe. With the ability to see now not handiest the mistake
itself. Now, I will stay issues contrived right here to make it easy, however
persist with me right here. What if I really like that statement such a lot (or I’ve a
choice of assertions) that I wish to summary it away right into a serve as so I
can use it in a host of various checks? Let’s take a look at that:

const upload = (a, b) => a + b

serve as assertAdd(inputs, output) {
  be expecting(upload(...inputs)).toBe(output)
}

check('sums numbers', () => {
  assertAdd([1, 2], 4)
})

Please be mindful, I’m now not recommending you create unnecessary abstractions
like the only above. As with the whole lot, you will have to be making use of AHA
Programming
(and for trying out).
This weblog publish is solely helpful for eventualities the place the abstraction is obvious
and you need to incorporate assertions for your abstraction.

Alright, with this little abstraction, this is the mistake we get:

FAIL  ./upload.check.js
sums numbers (3 ms)

sums numbers

    be expecting(won).toBe(anticipated) // Object.is equality

    Anticipated: 4
    Won: 3

      2 |
      3 | serve as assertAdd(inputs, output)    be expecting(upload(...inputs)).toBe(output)
        
      6 |
      7 | check('sums numbers', () => {

      at assertAdd (upload.check.js:4:26)
      at Object.<nameless> (upload.check.js:8:3)

What!? That is not just about as useful! What if we had a host of puts we are
calling assertAdd? What just right is that codeframe going to do us? How do we all know
which one failed. Oh, there it’s, I we do get a line within the stack hint,
however… like… discuss a step backward. I might a lot relatively have the road that
referred to as assertAdd be what displays up within the codeframe.

Neatly, there is no API into Jest for this (but?), however you can drive Jest to offer
you a codeframe the place you need. So what I will display you subsequent is how we will
make this mistake output like this:

FAIL  ./upload.check.js
sums numbers (3 ms)

sums numbers

    be expecting(won).toBe(anticipated) // Object.is equality

    Anticipated: 4
    Won: 3

      14 |
      15 | check('sums numbers', () =>  )
      18 |

      at Object.<nameless> (upload.check.js:16:3)

? Cool. Let’s dive in.

In truth, it is lovely easy. Keep in mind the entire stack hint we had with common
node? Neatly, when the be expecting library throws an error, we get a complete stack hint
as smartly. Let’s take the contents of our assertAdd serve as and put it in a
take a look at/catch so we will take a look at the error.stack:

serve as assertAdd(inputs, output) {
  take a look at {
    be expecting(upload(...inputs)).toBe(output)
  } catch (error) {
    console.log(error.stack)
    throw error
  }
}

Here is what’s logged with that:

Error: be expecting(won).toBe(anticipated) // Object.is equality

Anticipated: 4
Won: 3
    at assertAdd (/Customers/kentcdodds/code/kentcdodds.com/upload.check.js:5:28)
    at Object.<nameless> (/Customers/kentcdodds/code/kentcdodds.com/upload.check.js:17:3)
    at Object.asyncJestTest (/Customers/kentcdodds/code/kentcdodds.com/node_modules/jest-jasmine2/construct/jasmineAsyncInstall.js:100:37)
    at /Customers/kentcdodds/code/kentcdodds.com/node_modules/jest-jasmine2/construct/queueRunner.js:47:12
    at new Promise (<nameless>)
    at mapper (/Customers/kentcdodds/code/kentcdodds.com/node_modules/jest-jasmine2/construct/queueRunner.js:30:19)
    at /Customers/kentcdodds/code/kentcdodds.com/node_modules/jest-jasmine2/construct/queueRunner.js:77:41
    at processTicksAndRejections (inside/procedure/task_queues.js:93:5)

That error.stack has already gotten some useful remedy from Jest’s
be expecting statement library (it is even were given useful colours at this level).

Word that error.stack is in reality a mix of the error.message + the
stack hint, so the mistake message that be expecting supplies is the whole lot above
the primary “at” line which is the place the stack hint in reality begins.

Good enough, so you can realize that the stack hint we’ve got were given right here may be very other from
the one who Jest displays us. It is because lots of the stuff in there’s lovely
unnecessary to builders. It is simply noise. Why do builders wish to know that
their code ran thru mapper serve as at queueRunner.js:30:19? Yeah, lovely
unnecessary. So
when Jest codecs the stack hint,
it
filters out a host of the noise,
and we are left with:

Error: be expecting(won).toBe(anticipated) // Object.is equality

Anticipated: 4
Won: 3
    at assertAdd (/Customers/kentcdodds/code/kentcdodds.com/upload.check.js:5:28)
    at Object.<nameless> (/Customers/kentcdodds/code/kentcdodds.com/upload.check.js:17:3)

For sure extra useful. The following factor Jest does is
it takes the primary line
in the remainder stack hint traces and
creates the codeframe for the primary line.
Then it
codecs filepaths
and we are left with the moderately unnecessary error + codeframe + stack hint proven
above.

So, working out that, the answer is lovely easy: make certain that the primary
related line in our stack hint is the only we would like within the codeframe!

So, what we wish to do, is filter the one who contains the serve as
assertAdd and we are off the races:

serve as assertAdd(inputs, output) {
  take a look at {
    be expecting(upload(...inputs)).toBe(output)
  } catch (error) {
    error.stack = error.stack
      // error.stack is a string, so let's cut up it into traces
      .cut up('n')
      // filter the road that comes with assertAdd (that you must make this extra tough via the use of your check utils filename as a substitute).
      .clear out(line => !line.contains('assertAdd'))
      // sign up for the traces again up right into a unmarried (multiline) string
      .sign up for('n')
    throw error
  }
}

And with that we get the stack hint I previewed to you above. Here is a
screenshot of that:

visual representation of the good output

The issue with that is we in reality do not wish to simply filter our
software. What if that software serve as is constructed on best of alternative purposes. So
in reality, we wish to take away the whole lot above our software as smartly. That is
in reality what Jest’s be expecting does and
it makes use of Error.captureStackTrace.

Let’s take a look at that:

serve as assertAdd(inputs, output) {
  take a look at {
    be expecting(upload(...inputs)).toBe(output)
  } catch (error) {
    Error.captureStackTrace(error, assertAdd)
    throw error
  }
}

Wow, that is so much cleaner. So we cross the error we would like up to date and the
serve as we would like got rid of from the stack hint. That argument is known as the
constructorOpt.
Consistent with the Node.js medical doctors:

The not obligatory constructorOpt argument accepts a serve as. If given, all
frames above constructorOpt, together with constructorOpt, will likely be ignored
from the generated stack hint.

It is nearly as though this have been created for our actual use case!

So right here it’s all in combination:

const upload = (a, b) => a + b

serve as assertAdd(inputs, output) {
  take a look at {
    be expecting(upload(...inputs)).toBe(output)
  } catch (error) {
    Error.captureStackTrace(error, assertAdd)
    throw error
  }
}

check('sums numbers', () => {
  assertAdd([1, 2], 4)
})

And this is the output:

FAIL  ./upload.check.js
sums numbers (3 ms)

sums numbers

    be expecting(won).toBe(anticipated) // Object.is equality

    Anticipated: 4
    Won: 3

      11 |
      12 | check('sums numbers', () =>    assertAdd([1, 2], 4)
         )
      15 |

      at Object.<nameless> (upload.check.js:13:3)

And here is what that appears like visually:

visual representation of the error message

One more thing to notice is that Jest mechanically is aware of not to make a codeframe
out of a line that is coming from node_modules. So in case you post your
utilities to npm, you most likely do not wish to trouble filtering issues out
your self. That is in reality handiest helpful for the ones trying out abstractions you in finding
your self writing in a testbase at scale.

However manipulating the stack hint for advanced error messages will also be just right
wisdom to have, even for belongings you post to a registry. For instance,
DOM Trying out Library does this in waitFor
to verify disasters of asynchronous utilities (like in finding* queries and
waitFor itself) have stunning mistakes and good stack lines (async stack
lines are lovely unnecessary).

waitFor works

  TestingLibraryElementError: Not able to in finding an part with the textual content: /not anything suits this/. This may be as a result of the textual content is damaged up via a couple of components. In this case, you can supply a serve as for your textual content matcher to make your matcher extra versatile.

    <frame />

      2 |
      3 | check('waitFor has a pleasing stack hint', async () =>  )

      at waitForWrapper (node_modules/@trying out-library/dom/dist/wait-for.js:94:27)
      at Object.<nameless> (upload.check.js:4:9)

I am hoping that is helping you know how to make the mistake messages higher for
customized utilities you’re making on your checks! Excellent success.

[ad_2]

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back To Top
0
Would love your thoughts, please comment.x
()
x