node package manager

@myndzi/test-runner

Test-runner

It runs tests. And stuff.

Usage

npm install --save-dev @myndzi/test-runner

package.json:

"scripts": {
    "test": "test-runner"
}

Run tests:

npm test [filters] [-- --switches]

Command line arguments

Most of these will be explained further below.

  • -q --quiet - sets log level to 'none'
  • -v --verbose - sets log level to 'debug'
  • -V --trace - sets log level to 'trace'
  • -r --do-report - enables report output
  • -c --do-cov - enables coverage report
  • -l --do-lint - enables jshint
  • -P --perf-only - do a performance test only, no unit tests, etc.
  • -L --lint-only - run jshint only, no unit tests, etc.
  • --spec - use spec reporter
  • --color - enable color
  • --bail - stop running tests after first error
  • --base-dir - specify the app base dir
  • --report-dir - specify the report dir

Any of the boolean switches can be negated by prefixing with no-, for example, --no-color will disable color output. Single-letter arguments can be crammed together in typical Linux fashion, e.g. -rpc

Note:

Since npm can accept arguments itself, it is necessary to distinguish between 'arguments to be given to npm' and 'arguments to be given to test-runner' when using npm to run tests. The usual way to do that sort of thing in Linux is to separate the arguments with --

Therefore, if you wanted to use the npm test approach (and you do, because it's the only helpful way to run a local module without specifying a full path to its binary script!), you must do it like this when specifying switches:

npm test -- -rpc --report-dir=./foo

Thankfully this is not necessy for using filters:

npm test services

The above works just fine. npm >= 2.0 is required for this behavior

Note:

Log level defaults to 'warn'; log level is passed on to all subtasks

Configuration

There are some defaults but they're not that useful. You'll likely want to create ./test/.runner.json; this file configures a few things about where the tests are and where the code is. The current configuration keys are:

  • preload - a .js file to load and execute before running any tests
  • unload - a .js file to load and execute after running all tests
  • perfTest - a .js file to load and execute to run a performance test
  • soureDirs - an array of strings relative to <appRoot> for directories that contain source code to instrument for coverage and to lint
  • testDirs - an ordered array of strings relative to <appRoot>/test for directories that contain tests to run
  • subModules - an array of module names to also run tests on, if possible

Note:

preload and unload scripts should export a function like so:

module.exports = function () {
};

The perfTest script should export a function, and is passed a couple arguments like so:

module.exports = function (baseDir, opts) {
};

Where baseDir is the application base dir (not the test dir), and opts may contain logLevel (a log level threshold), doReport (a boolean, whether to write a report), and reportDir (a string, where to write the report to).

perfTest should return a promise that resolves when the test is complete and the report has been written.

Test ordering

By default, tests exit early at the first failure. Tests are run in the order of the testDirs specified in the config file. Ideally, the fastest tests (e.g. unit tests) come first and the slowest tests (e.g. integration tests) come last; this enables the test run to fail as fast as possible in the case of failure.

If subModules is specified, these tests are run prior to the above. This enables simultaneous development of packages that depend on other packages. test-runner will look for .runner.json in these other packages' own test directories to determine what to do. If submodule tests fail, the process will exit before running any tests of the module proper.

Filtering

To further aid in easily targeting and running specific tests, test-runner implements a filtering system that enables you to run selected subsets of your tests; this is useful for example when you are trying to make a specific test pass and running the entire suite takes a while. To use filtering, simply supply keywords to filter by as arguments:

npm test foo
npm test foo bar baz

Specified filters are intersected -- only tests matching all specified filters will be run.

Filtering is a two-step process involving test location and test filename. All the given filters are compared against submodule names and test directory names first; matches will confine the tests being run to those locations. Any remaining filters are used to filter out specific files by matching against .-separated tokens in the filename.

To give a concrete example, npm test unit would load all test files in the ./test/unit directory, provided it exists and is specified as a testDir in the config. Similarly, npm test logger would run against the logger submodule, provided it was specified in the subModules key of the config, existed (was installed into node_modules), and contained a test directory (possibly with its own .runner.json config file).

Filename-wise, npm test services locations would load all tests in the file ./test/services/locations.test.js but ignore ./test/unit/locations.test.js and ./test/services/categories.test.js

It's a bit complicated to explain, but simple to use; in practice, just include enough keywords to target what you want. You can cross-cut your test directories and target a specific group of functionality to run unit and integration tests, or you can target just unit tests for everything, or just integration tests, and so on.

Reporting

By default, no reports are created. If reporting is enabled, an xUnit compatible xml file is created for the results of the tests and a Cobertura compatible xml file is created for the results of the coverage tests.

Performance test results are up to the specified performance test module to create; sites-backend creates xml files that are usable by the Jenkins Plot plugin.

Programmatic use

var Runner = require('test-runner');
var runner = new Runner(opts, baseDir);
runner.run().then(...).catch(...);

opts are mostly as the command line switches, but are camelCased (--do-cov -> doCov). You can pass subtask-specific options here, too:

var runner = new Runner({
    mocha: {
        logLevel: 'trace',
        doReport: false
    },
    perf: {
        logLevel: 'none',
        doReport: true
    }
});

Valid subtasks are 'mocha', 'coverage', 'perf', and 'lint'