Mocha: Shorten stack traces

Created on 22 Aug 2012  Â·  52Comments  Â·  Source: mochajs/mocha

80% of the AssertionError stack trace is uninteresting mocha internal code

Suggestion:
suppress all frames from Test.Runnable.run and previous

Suggestion:

  1. Reformat the stack trace so the first text is base-file:line:column, then Object.function, then directory.
  2. Directory can be shortened to be relative to the deploy folder (ie. where app.js is)

I am using node assert, ui exports and spec reporter

feature help wanted

Most helpful comment

in the mean time, mocha-clean is still around.

All 52 comments

yeah I'd like to have this too, at least as a default (so I can still get those callsites if it really is a mocha bug)

+1

Also highlighting the test that failed in the stack trace would be sweet

that too, I think I have a branch with that going

I implemented the short stack traces in the assert-wrap I use in mochawrapper 0.0.22

It also has a variant of character escaping you might find interesting.

I have since written a stack trace parser here: https://github.com/haraldrudell/haraldutil/blob/master/lib/stacktraceparser.js
It could make this improvement easier to accomplish.

+1

+1

+1

+1

+1
https://github.com/visionmedia/mocha/issues/815#issuecomment-33887977

How do we move this forward? I just looked at mocha code for the last 2 hours trying to figure out how to shorten the output to a single line of

      at Context.<anonymous> (./public/tests/_prettyfy.js:10:25)

and the closest I came was removing the stack trace completely by removing "stack" from line 205 of mocha/lib/reporters/base.js like this:

    // ( line ~201 )
    // indent stack trace without msg
    stack = stack.slice(index ? index + 1 : index)
      .replace(/^/gm, '  ');

    console.error(fmt
    , (i + 1)
    , test.fullTitle()
    , msg
    //, stack <====== commenting this dude out.
    );
  });

which will output this:

Running "Mocha" task
 1   -_-_,------,
 1   -_-_|   /\_/\ 
 0   -_-^|__( x .x) 
     -_-  ""  "" 

  1 passing (15ms)
  1 failing

  1) mocha will fail:
     Error: expected 1 to equal 0
%s

...
So... close...
I don't know where the %s comes from but it kind of looks cool if you get a bunch of them in a line

%s%s%s%s%s%s____✬
%s%s%s%s%s%s%s%s%s%s____✬
%s%s%s%s%s%s%s%s%s____✬
%s%s%s%s%s%s____✬

pewpewpew

p.s.
I'm using mocha because of nyancat and growl-- whoever made the decision to add those two reporters as defaults is a genius.

+1

I'm getting pages and pages of this:

screen shot 2014-04-11 at 2 39 36 pm

@lynndylanhurley looks like you're using one of the long stacktrace libs? Maybe we could dedupe and display at <something> (x 1500) haha, just truncating isn't overly useful

@visionmedia - de-duping would be great :+1:

I made a quick fix without file modification, might not good for every situation since it removes an argument from console error

console.oldError = console.error;
console.error = function (args) {
    if (typeof arguments.stack !== 'undefined') {
        console.oldError.call(console, arguments.stack);
    } else {
        if (typeof arguments[4] !== 'undefined') arguments[4] = "...";
        console.oldError.apply(console, arguments);
    }
 }

@LittleG: Nice hack! Thank you. Made it filter stacktrace lines.

I stumbled upon this today and thanks to @LittleG I was able to tweak my setup. @LittleG's solution removes the entire stack trace, but I updated it so it just shows the first 4 lines of it:

console.oldError = console.error;
console.error = function (args) {
  if (typeof arguments.stack !== 'undefined') {
    console.oldError.call(console, arguments.stack);
  } else {
    if (typeof arguments[4] !== 'undefined') {
      var traceToShow = arguments[4].split('\n').slice(0, 4);
      arguments[4] = traceToShow.join('\n');
    }
    console.oldError.apply(console, arguments);
  }
}

@LittleG and @jackfranklin thanks, that was great!

I've modified it slightly to further reduce noise.
The use of "global" is to support mocha -w code reloading.

/* Make stack traces shorter and more relevant
 * by cutting full path to app and reducing height */
var path = require('path');
var appRoot = path.resolve(__dirname, '..')+'/';
console.oldError = global.oldError || console.error;
console.error = function (args) {
  if (typeof arguments.stack !== 'undefined') {
    console.oldError.call(console, arguments.stack);
  } else {
    if (typeof arguments[4] !== 'undefined') {
      var traceToShow = arguments[4].split('\n').slice(0, 4);
      arguments[4] = traceToShow.join('\n').replace(RegExp(appRoot, 'g'), '');
    }
    console.oldError.apply(console, arguments);
  }
}
global.oldError = console.oldError;

ok cool, you guys can use/extend this now, built from this discussion ...
https://www.npmjs.org/package/simple-stacktrace / https://github.com/keyvanfatehi/js-simple-stacktrace

v0.2.0 now ships with a bin and prefers that you use it as a pipe

e.g. mocha -w --recursive test/unit -R min 2>&1 | simple-stacktrace

works pretty damn well

Hi, I do something similar to the keyvanfatehi solution:

I'm developing a package for httprequesttests ( mocha + chai + superagent ) and put this in the index.js, also give the users the opportunity of change errorStackDepth by config.

var
oldconsoleError = console.error,
errorStackDepth = 1;

console.error = function () {//fmt, i, title, msg, stack
var
args = Array.prototype.slice.call( arguments, 0 ),
stack = args[ 4 ];

if ( stack ) {
    args[ 4 ] = stack.split( '\n' ).slice( 0, errorStackDepth ).join( '\n' );
}

oldconsoleError.apply( console, args );

};

Here's a more elegant solution to cleaning up stack traces. simple add -r mocha-clean to your invocation (or to the _test/mocha.opts_ file).

https://github.com/rstacruz/mocha-clean

you're my hero

On Wed, Sep 24, 2014 at 11:51 PM, Rico Sta. Cruz [email protected]
wrote:

Here's a more elegant solution to cleaning up stack traces.

https://github.com/rstacruz/mocha-clean

Reply to this email directly or view it on GitHub:
https://github.com/visionmedia/mocha/issues/545#issuecomment-56773486

I'd like to see this in Mocha. Too much garbage in the stack.

+1

@boneskull, would you be interested in merging mocha-clean features into mocha?

@rstacruz Looks cool, I'll have to take a closer look and see what kind of effort it would take.

awesome.

if I may also suggest: at the very root of it, mocha-clean works like this:

/* monkey patch the fail() method */
Runner.prototype.fail = function (test, err) {
  cleanError(err);
  /* continue on with current behavior */
}

function cleanError (err) {
  err.stack = /* remove lines matching /node_modules/, and so on */;
};

perhaps it would be nice to have a generic interface for inspecting errs that pass thru mocha too.

Oh please, please - let's filter out mocha frames from the printed stack traces. With the current way of reporting traces we've got way too much noise flowing through a screen so it is hard to quickly see what / why is failing.

mocha-clean sounds like a good idea but requires additional installation ceremony (https://github.com/rstacruz/mocha-clean/blob/master/docs/Using_with_mocha_loaders.md), especially if you use it with gulp / grunt.

I would be willing to put some effort into this issue if anyone wants to pair or cary to give directions in terms of a preferred solution...

If nothing else we could expose something like formatStackTrace on the base reporter so people can override it as needed.

@rstacruz :clap:

So awesome!

Any news about merging mocha-clean into mocha?

@dasilvacontin Fork it, merge it, create PR, advertise it, use it, fix it, then either everyone's on your fork or it gets merged in. Enjoy your burrito.

I want a burrito. Let's see.

I think a lot of us stopped at @rstacruz's fix and enjoyed our burritos then. I'd happily try out your fork and enjoy a burrito deluxe.

I think we can close this issue @travisjeffery.

This is not fixed at all.

module.js:339
    throw err;
    ^
Error: Cannot find module '../../lib/camelize'
    at Function.Module._resolveFilename (module.js:337:15)
    at Function.Module._load (module.js:287:25)
    at Module.require (module.js:366:17)
    at require (module.js:385:17)
    at Object.<anonymous> (/Users/ara/dev/iteam/dm-crawler/tests/unit/lib/camelize.js:5:16)
    at Module._compile (module.js:435:26)
    at Object.Module._extensions..js (module.js:442:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:311:12)
    at Module.require (module.js:366:17)
    at require (module.js:385:17)
    at /Users/ara/dev/iteam/dm-crawler/node_modules/mocha/lib/mocha.js:216:27
    at Array.forEach (native)
    at Mocha.loadFiles (/Users/ara/dev/iteam/dm-crawler/node_modules/mocha/lib/mocha.js:213:14)
    at Mocha.run (/Users/ara/dev/iteam/dm-crawler/node_modules/mocha/lib/mocha.js:453:10)
    at Object.<anonymous> (/Users/ara/dev/iteam/dm-crawler/node_modules/mocha/bin/_mocha:393:18)
    at Module._compile (module.js:435:26)
    at Object.Module._extensions..js (module.js:442:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:311:12)
    at Function.Module.runMain (module.js:467:10)
    at startup (node.js:134:18)
    at node.js:961:3
npm ERR! Test failed.  See above for more details.

all the trace is hugely irrelevant to my code.

haha.. i forgot how this was resolved... but i think it was rolled back at some point

Admittedly, it was partially rolled back and it may have been a knee-jerk reaction on my part to some of the issues we were seeing at that time. We do partial stack trace filtering in an attempt to remove Mocha's internals from the results. However, we don't hide any code living in other modules (under node_modules). Maybe this is something that could be revisited.

+1 please re-implement this.

in the mean time, mocha-clean is still around.

Yep, I am using it now. Just a shame really that this simple plugin does exactly what is needed, yet fails to have been merged into mocha and that this issue has been around since August 2012... :(

It was merged, but the filtering was minimized after complaints that test output was no longer helpful in debugging. I think the simple issue is that it shouldn't have been the default behavior, and should instead be opt-in.

Agreed that it can be opt-in. I believe mocha-clean also has options to exclude files in node_modules from being filtered out, I assume this could be tweaked to exclude certain other directories as well. Or simply only ignore mocha internals.

Perhaps a .mocharc would provide more flexibility than .mochaopts in terms of configuration.

There's a --full-trace flag which will result in even longer stack traces. I thought the core issues had been mostly resolved, though edge cases exist (e.g. when using compilers, etc). See below:

$ cat example.js
var assert = require('assert');

it('test', function() {
  function foo() {
    assert(false);
  }

  foo();
});

And running it:

$ mocha example.js


  1) test

  0 passing (10ms)
  1 failing

  1)  test:

      AssertionError: false == true
      + expected - actual

      -false
      +true

      at foo (example.js:5:5)
      at Context.<anonymous> (example.js:8:3)

For comparison:

dstjules:~
$ mocha --full-trace example.js


  1) test

  0 passing (9ms)
  1 failing

  1)  test:

      AssertionError: false == true
      + expected - actual

      -false
      +true

      at foo (/Users/dstjules/example.js:5:5)
      at Context.<anonymous> (/Users/dstjules/example.js:8:3)
      at callFn (/usr/local/lib/node_modules/mocha/lib/runnable.js:315:21)
      at Test.Runnable.run (/usr/local/lib/node_modules/mocha/lib/runnable.js:308:7)
      at Runner.runTest (/usr/local/lib/node_modules/mocha/lib/runner.js:422:10)
      at /usr/local/lib/node_modules/mocha/lib/runner.js:533:12
      at next (/usr/local/lib/node_modules/mocha/lib/runner.js:342:14)
      at /usr/local/lib/node_modules/mocha/lib/runner.js:352:7
      at next (/usr/local/lib/node_modules/mocha/lib/runner.js:284:14)
      at Immediate._onImmediate (/usr/local/lib/node_modules/mocha/lib/runner.js:320:5)
      at processImmediate [as _immediateCallback] (timers.js:357:17)

So, it looks like the original issue has been resolved. That is:

80% of the AssertionError stack trace is uninteresting mocha internal code

This can be seen in my previous comment.

As for https://github.com/mochajs/mocha/issues/545#issuecomment-182321085 part of the stack trace could be cleaned, but only half the noise is from mocha itself:

$ cat example.js
var failed = require('failed');

$ node example.js
module.js:318
    throw err;
          ^
Error: Cannot find module 'failed'
    at Function.Module._resolveFilename (module.js:316:15)
    at Function.Module._load (module.js:258:25)
    at Module.require (module.js:345:17)
    at require (module.js:364:17)
    at Object.<anonymous> (/Users/dstjules/example.js:1:76)
    at Module._compile (module.js:410:26)
    at Object.Module._extensions..js (module.js:428:10)
    at Module.load (module.js:335:32)
    at Function.Module._load (module.js:290:12)
    at Function.Module.runMain (module.js:451:10)

I'm not really sure we should be in the business of filtering stack traces. It's too easy to filter too much or not enough. When you add something like trace and/or clarify you start stepping on toes.

I propose an alternative: _highlight_ each line of the stack that is within the code under test or the test files itself. JetBrains' reporter does this. Hopefully, that won't be too difficult to implement.

It could look something like the following:

2016-03-16_2202

Opinions?

It's too easy to filter too much or not enough.

Not really.

  • Too much: more than the part of the stack trace that is mocha internals
  • Too little: including mocha internals portion in the stack trace

By default, cleaner output. Provide a flag for full stack traces. It's what tj said even he wanted.

My opinion is that mocha-clean solved this for me and it's rather silly that mocha's default output is still cluttered.

Filtering sounds all well and good, but I for one would love to have an option to either mute stack traces or truncate the stack trace to a maximum number of lines (instead of or in addition to the kind of filtering I see discussed here), for those of us that make extensive use of recursion, or use libraries like jsverify that do so. When your stack traces are hundreds of lines long, even if it's all your code, more than the first, oh, thirty lines or so is not likely to be helpful in fixing a test. As I mentioned in #2114, I don't know whether to suggest this be a reporter option or a core mocha option; guidance would be appreciated.

+1

long-since done

I have mocha running in the browser to display tests following a setup I wrote here https://medium.com/@Kagerjay/portable-javascript-unit-testing-with-mocha-and-chai-e2e468dbeccc,

I ended up just using CSS to limit the size of the stacktrace on the browser.

Its a lazy solution but it works

.error {
  max-height: 30px !important;
}

Example of how the stacktrace looks on my browser now

vztctsi 1

Tested under: Browser (Chrome 69.0.3497.100)

Issue: mocha.js internal stack trace isn't hidden when the file has a different name (e.g. mocha.min.js)


Hi @boneskull, I appreciate that the internal stack trace has been hidden for the standard use case, but I've encountered that any change to the filename of mocha.js (e.g. to mocha.min.js, as in @vincentntang's example above) prevents that filtering from happening.

I think there's a simple solution, at least within browsers, by detecting the name of the script using document.currentScript:

// Gets entire path and name of the current script.
var filename = document.currentScript.src;

A possible downside would be a loss of granularity on heavily bundled apps. Another approach could be to simply include mocha.min.js (or regex mocha.*\.js to also match files with version numbers) in the filtering. For the time being, @rstacruz's mocha-clean is necessary for anyone using a version with an altered filename.

P.S. I did test and the minified version is properly filtered when its name is changed to mocha.js, so it's exclusively a filename issue.

Was this page helpful?
0 / 5 - 0 ratings