'List all mocha tests without executing them

Is there a way in mochajs to list all tests collected by test runner without executing them?

E.g. if there are specs that look like:

describe('First', function() {
    it('should test something', function() {
        ...
    })
});

describe('Second', function() {
    it('should test something else', function() {
        ...
    })
});

then I want to get console output similar to an output produced by test reporters, but without executing actual tests, like this:

First
    should test something
Second
    should test something else

UPD:

Currently I'm extracting all describes and its with regex, but looking for a cleaner solution.



Solution 1:[1]

The mocha-list-tests package is useful, but only works for BDD style describe() and it(), and it breaks if you .skip() any tests because it mocks it().

One way to do this yourself if you need to overcome either of those things, or get other information on the tests, is to exploit Mocha's root before() hook. This will be executed after Mocha has loaded all files but before it executes any tests, so all the information you need exists at that point.

This way, it is pretty easy to patch in a --list-only command line option to switch the behaviour of your test run without having to add or change anything else.

The key is that this in the before() hook is Mocha's Context, and the .test of that refers to the hook itself. So this.test.parent refers to the root suite. From there, you can walk down the tree of .suites arrays, and .tests arrays of each suite.

Having collected whatever you want, you have to then output that and exit the process to stop Mocha from continuing.

Plain Text Example

Given root.js:

#!/bin/env mocha

before(function() {
    if(process.argv.includes('--list-only')) {
        inspectSuite(this.test.parent, 0);
        process.exit(0);
    }
    // else let Mocha carry on as normal...
});

function inspectSuite(suite, depth) {
    console.log(indent(`Suite ${suite.title || '(root)'}`, depth));

    suite.suites.forEach(suite => inspectSuite(suite, depth +1));
    suite.tests.forEach(test => inspectTest(test, depth +1));
}

function inspectTest(test, depth) {
    console.log(indent(`Test ${test.title}`, depth));
}

function indent(text, by) {
    return '    '.repeat(by) + text;
}

And test.js:

#!/bin/env mocha

describe('foo', function() {
    describe('bar', function() {
        it('should do something', function() {
            // ...
        });
    });

    describe('baz', function() {
        it.skip('should do something else', function() {
            // ...
        });

        it('should do another thing', function() {
            // ...
        });
    });
});

Then running mocha as normal would give you the test results you expect:

  foo
    bar
      ? should do something
    baz
      - should do something else
      ? should do another thing


  2 passing (8ms)
  1 pending

But running mocha --list-only would give you (without running any tests):

Suite (root)
    Suite foo
        Suite bar
            Test should do something
        Suite baz
            Test should do something else
            Test should do another thing

JSON Example

root.js

#!/bin/env mocha

before(function() {
    let suites = 0;
    let tests = 0;
    let pending = 0;

    let root = mapSuite(this.test.parent);

    process.stdout.write(JSON.stringify({suites, tests, pending, root}, null, '    '));
    process.exit(0);

    function mapSuite(suite) {
        suites += +!suite.root;
        return {
            title: suite.root ? '(root)' : suite.title,
            suites: suite.suites.map(mapSuite),
            tests: suite.tests.map(mapTest)
        };
    }

    function mapTest(test) {
        ++tests;
        pending += +test.pending;
        return {
            title: test.title,
            pending: test.pending
        };
    }
});

With the same test script as before would give you:

{
    "suites": 3,
    "tests": 3,
    "pending": 1,
    "root": {
        "title": "(root)",
        "suites": [
            {
                "title": "foo",
                "suites": [
                    {
                        "title": "bar",
                        "suites": [],
                        "tests": [
                            {
                                "title": "should do something",
                                "pending": false
                            }
                        ]
                    },
                    {
                        "title": "baz",
                        "suites": [],
                        "tests": [
                            {
                                "title": "should do something else",
                                "pending": true
                            },
                            {
                                "title": "should do another thing",
                                "pending": false
                            }
                        ]
                    }
                ],
                "tests": []
            }
        ],
        "tests": []
    }
}

Solution 2:[2]

Wrap all your describe blocks in a describe block and skip it.

describe.skip('Outline', function() {
    describe('First', function() {
        it('should test something', function() {
            ...
        })
    });

    describe('Second', function() {
        it('should test something else', function() {
            ...
        })
    });
});

Solution 3:[3]

As of mocha@9, the dry-run option (PR here) has been added.

If you want to list (and programmatically make use of) all tests, you want to enable dry-run and probably the json reporter + an output filepath like so:

{
  "dry-run": true,
  "reporter": "json",
  "reporterOptions": [
    "output": "./test-report.json"
  ]
}

How to produce a test list that we can use programmatically?

If you want to use the test output programmatically:

As of this commit (Aug 2021), you can just provide the output file config option, and then read the json file from your test processing program.

tough luck. Turns out, the JSON reporter, contrary to its name, does not produce pure JSON (as discussed in this outstanding Mocha issue). As a workaround, I wrote a little script (gist here), that works like so:

  1. Store mocha output to file: npm run test > tests-raw.json
    • Or: yarn test > tests-raw.json
    • Or: mocha --dry-run --reporter=json ... > tests-raw.json
  2. Convert the output: ./mocha-list-tests.js tests-raw.json tests.json
  3. The file tests.json now contains an array of all the test result objects. Each call to mocha that was recorded should yield one such object.

More Config Options

I could not find much documentation on the exact output format, but it's mostly just two things:

  • stats - basic overall stats
  • tests - all found tests (see Test#serialize method)
  • failures, pending, passes - you probably don't care about these if you only want to list tests.

Solution 4:[4]

mocha-list-tests package

I kid you not, there is a separate package for that: https://www.npmjs.com/package/mocha-list-tests

npm install mocha mocha-list-tests
node ./node_modules/mocha-list-tests/mocha-list-tests.js main.js

Sample output:

{
  "suites": [
    "a1",
    "a2"
  ],
  "tests": [
    "a1.a11",
    "a1.a12",
    "a2.a21",
    "a2.a22"
  ],
  "tree": {
    "a1": {
      "a11": true,
      "a12": true
    },
    "a2": {
      "a21": true,
      "a22": true
    }
  }
}

for this test file:

main.js

#!/usr/bin/env node

const assert = require('assert');
const fs = require('fs');

describe('a1', function() {
  it('a11', function() {
    assert.equal(1, 1);
    fs.writeFileSync('abc', 'def', 'utf8');
  });
  it('a12', function() {
    assert.equal(1, 2);
  });
});
describe('a2', function() {
  it('a21', function() {
    assert.equal(1, 1);
  });
  it('a22', function() {
    assert.equal(1, 2);
  });
});

The file abc was not created, so I know that tests were not executed.

This is likely using the API Mocha mentioned at: https://github.com/mochajs/mocha/wiki/Using-Mocha-programmatically

Teste in [email protected], [email protected], Node v10.15.1.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 dNitro
Solution 3
Solution 4 Ciro Santilli Путлер Капут 六四事