Mocha JSON Report(er)

Pavel Saman
3 min readNov 11, 2022

--

Photo by Blake Connally on Unsplash

In Mocha, popular JavaScript test framework, you can change the default spec reporter. There are a few options included by default, you can also install various 3rd party reporters you can find e.g. in NPM repository. However, if you want to process test results, JSON is probably a format to go and the default JSON reporter should get you all important info.

To configure Mocha, you can use .mocharc.json. To use the JSON reporter, add the following two properties to the file:

{
"reporter": "json",
"reporterOptions": [
"output=./results.json"
]
}

After you run your tests, you should see results.json file in the root of the project. A sample output can look like:

{
"stats": {
"suites": 1,
"tests": 7,
"passes": 6,
"pending": 0,
"failures": 1,
"start": "2022-11-11T19:43:36.376Z",
"end": "2022-11-11T19:43:36.381Z",
"duration": 5
},
"tests": [
{
"title": "return string",
"fullTitle": "formatDay() return string",
"file": "/home/pavel/testing/useful/tests/formatDay.test.js",
"duration": 1,
"currentRetry": 0,
"speed": "fast",
"err": {}
},
{
"title": "return correct format",
"fullTitle": "formatDay() return correct format",
"file": "/home/pavel/testing/useful/tests/formatDay.test.js",
"duration": 0,
"currentRetry": 0,
"speed": "fast",
"err": {}
},
{
"title": "month undefined",
"fullTitle": "formatDay() month undefined",
"file": "/home/pavel/testing/useful/tests/formatDay.test.js",
"duration": 0,
"currentRetry": 0,
"speed": "fast",
"err": {}
},
{
"title": "month null",
"fullTitle": "formatDay() month null",
"file": "/home/pavel/testing/useful/tests/formatDay.test.js",
"duration": 1,
"currentRetry": 0,
"err": {
"message": "expected undefined to equal 2",
"showDiff": true,
"expected": 2,
"operator": "strictEqual",
"stack": "AssertionError: expected undefined to equal 2\n at Context.<anonymous> (file:///home/pavel/testing/useful/tests/formatDay.test.js:28:39)\n at process.processImmediate (node:internal/timers:471:21)"
}
},
{
"title": "month primitive",
"fullTitle": "formatDay() month primitive",
"file": "/home/pavel/testing/useful/tests/formatDay.test.js",
"duration": 0,
"currentRetry": 0,
"speed": "fast",
"err": {}
},
{
"title": "month string",
"fullTitle": "formatDay() month string",
"file": "/home/pavel/testing/useful/tests/formatDay.test.js",
"duration": 0,
"currentRetry": 0,
"speed": "fast",
"err": {}
},
{
"title": "month object",
"fullTitle": "formatDay() month object",
"file": "/home/pavel/testing/useful/tests/formatDay.test.js",
"duration": 0,
"currentRetry": 0,
"speed": "fast",
"err": {}
}
],
"pending": [],
"failures": [
{
"title": "month null",
"fullTitle": "formatDay() month null",
"file": "/home/pavel/testing/useful/tests/formatDay.test.js",
"duration": 1,
"currentRetry": 0,
"err": {
"message": "expected undefined to equal 2",
"showDiff": true,
"expected": 2,
"operator": "strictEqual",
"stack": "AssertionError: expected undefined to equal 2\n at Context.<anonymous> (file:///home/pavel/testing/useful/tests/formatDay.test.js:28:39)\n at process.processImmediate (node:internal/timers:471:21)"
}
}
],
"passes": [
{
"title": "return string",
"fullTitle": "formatDay() return string",
"file": "/home/pavel/testing/useful/tests/formatDay.test.js",
"duration": 1,
"currentRetry": 0,
"speed": "fast",
"err": {}
},
{
"title": "return correct format",
"fullTitle": "formatDay() return correct format",
"file": "/home/pavel/testing/useful/tests/formatDay.test.js",
"duration": 0,
"currentRetry": 0,
"speed": "fast",
"err": {}
},
{
"title": "month undefined",
"fullTitle": "formatDay() month undefined",
"file": "/home/pavel/testing/useful/tests/formatDay.test.js",
"duration": 0,
"currentRetry": 0,
"speed": "fast",
"err": {}
},
{
"title": "month primitive",
"fullTitle": "formatDay() month primitive",
"file": "/home/pavel/testing/useful/tests/formatDay.test.js",
"duration": 0,
"currentRetry": 0,
"speed": "fast",
"err": {}
},
{
"title": "month string",
"fullTitle": "formatDay() month string",
"file": "/home/pavel/testing/useful/tests/formatDay.test.js",
"duration": 0,
"currentRetry": 0,
"speed": "fast",
"err": {}
},
{
"title": "month object",
"fullTitle": "formatDay() month object",
"file": "/home/pavel/testing/useful/tests/formatDay.test.js",
"duration": 0,
"currentRetry": 0,
"speed": "fast",
"err": {}
}
]
}

There’s the stats object with some summary information about the whole test run. If you need to check that no tests have failed, you can check the stats.failuresvalue. A good thing is that if there’s been a failure in hooks, Mocha will increment this value as well.

tests array includes all executed tests — failed, passed, and pending (skipped). If a test failed, details will be in the err object. One caveat is that if a test is skipped, the corresponding object will have no duration property.

If you want only passed tests, parse passed array. I don’t know why objects in this array have the err property, though. If tests passed, I’d expect this object to be empty, so at this point, I have no idea if the property is included just for the consistency sake (but then why not include durationwith skipped tests?).

failures array is interesting because it containes both failed tests and other parts like failed hooks. If you want to get all failures encountered during a test run, parsing this array is a way to go.

Finally pendingarray should include skipped tests. Mocha marks everything skipped as pending.

That said, if you need to parse this file because, for example, you want to send the results somewhere else (e.g. to some monitoring platform like New Relic), then I’d say get all failures from failures and then get only passed and skipped tests from tests, e.g.:

const isFailed = (testCase) => (Object.keys(testCase.err).length === 0 ? false : true);

That way, you won’t end up having failed tests reported twice.

--

--