node-coverage v2.1.0
node-coverage
node-coverage is a tool that measures code coverage of JavaScript application.
Code coverage is a measure typically used in software testing to describe the degree to which the source code has been tested. This is an indirect measure of quality of your tests.
node-coverage can be used not only to extract measures on how well the application is covered by a test suite, but also to understand how much code is actually needed to load your application.
Coverage criteria
There are a large variety of coverage criteria. node-coverage measures
- statement coverage. Whether or not each statement has been executed.
- condition coverage. Whether or not each boolean sub-expression evaluated both to
trueandfalse. - decision coverage. For Javascript this implies from condition coverage.
- function coverage. Whether or not each functions has been called. Full statement coverage doesn't imply full function coverage when empty functions are used. An empty function has full statement coverage even when it's not called.
Why statement coverage is not enough? Consider the following code:
var dangerous = createAnObject();
if (dangerous != null) {
dangerous.doSomething();
}
dangerous.doSomethingElse();A test suite where dangerous is always different from null runs fine and achieve 100% statement coverage, however the program fails when dangerous is null.
Such test suite has only 50% of condition coverage because the condition dangerous != null is never evaluated false.
Note that for languages where boolean operators are not short-circuited, condition coverage does not necessarly imply decision coverage. This is not the case in JavaScript.
if (a && b) {
//...
}When a is false, b is not evaluated at all.
a = true, b = true
a = false, b = truehas 100% decision coverage because the if evaluates both to true and false but only 75% condition coverage because b never evaluates false.
Adding a test where
a = false, b = falsewon't increase condition coverage because the second condition (wheter b is true or not) is never checked by the language.
Prerequisites
node-coverage works instrumenting your JavaScript code and serving those instrumented files to your browser from a web server. Therefore it depends on
- Optimist library to parse command line arguments.
- UglifyJS to parse and instrument your files.
- Express to serve instrumented files.
- Jade a templating engine to display coverage reports.
- mkdirp utility for recursively create directories.
- Connect middleware layer
- node-http-proxy http proxy for node.js
Those dependencies can be installed (from the node-coverage directory) with:
npm installUnit tests run on Nodeunit.
The administrative interface uses for "Stats & Graph" page
- jQuery
- Highcharts charting library written in JavaScript
Usage
node server.js -d "/var/www" -r "/var/log/reports"This creates a server listenig on port 8080 serving the content of your folder /var/www and saving coverage reports inside /var/log/reports
Go to
http://localhost:8080and run your test suite. When complete you must call from your scripts the function
$$_l.submit()to submit the coverage report. The report is saved inside /var/log/reports as a JSON file.
To see the report go to the administrative interface on
http://localhost:8787It's also possible to specify a report name from the submit function
$$_l.submit("myTestCaseReport")Supported options
-hor--helplist of options-dor--doc-rootdocument root of the web server. All JS files in this folder will be instrumented. Default/var/www-por--portweb server port. Default8080-ror--report-dirdirectory where reports are stored. Default/var/log/node-coverage-aor--admin-portadministrative server port. Default8787--condition,--no-conditionEnable or disable condition coverage. By default it's enabled.--function,--no-functionEnable or disable function coverage. By default it's disabled.--static-infoIn case files are pre-instrumented, path to the JSON file containing static information about instrumented files.--session,--no-sessionEnable or disable storage of information not strictly needed by the browser. By default it's enabled. Disabling this means that more code is sent to and from the client.-ior--ignoreIgnore file or folder. This file/folder won't be instrumented. Path is relative to document root.--proxyProxy mode. You can use node-coverage to instrument files on a differnt host.--exit-on-submitThe default behavior is to keep the server running in order to collect multiple reports. By enabling this options the server will automatically shut down when a coverage report is received. This is useful for some continuous integration environment. If you want to collect more coverage reports but still be able to shut down the server when tests are done you can submit a request to '/node-coverage-please-exit'.-vor--verboseEnable more verbose logging information. Defaultfalse.
By default function coverage is disabled, to enable it you can run
node server.js --functionor
node server.js --no-conditionto disable condition coverage.
You can exclude some files or folders using
node server.js -i lib/minified -i lib/jquery.jsInstrumenting offline
The server instruments JavaScript files on each request. It's possible to instrument offline your files running
node instrument.js /var/www/myApp /var/www/myInstrumentedAppYou can then run the server with
node server.js -d /var/www/myInstrumentedAppSupported options
-hor--helplist of options-tottestrun unit tests--condition,--no-conditionenable or disable condition coverage. By default it's enabled.--function,--no-functionenable or disable function coverage. By default it's disabled.--static-infoPath to a JSON output file which will contain static information about instrumented files. Using this option reduces the size of instrumented files.-ior--ignoreIgnore file or folder. This file/folder is copied in target folder but not instrumented. Path relative to the source folder.-xor--excludeExclude file or folder. This file/folder won't be copied in target folder. Path relative to the source folder.
By default function coverage is disabled, to enable it you can run
node instrument.js --function /var/www/myApp /var/www/myInstrumentedAppor
node instrument.js --no-condition /var/www/myApp /var/www/myInstrumentedAppto disable condition coverage.
The code generated offline is equal to the one generated by the server when storage is disabled with --no-session, unless --static-info is used.
You can also instrument a single file launching
node instrument.js myScript.jsThe output is sent to standard input.
The command
node instrument /var/www/myApp /var/www/myInstrumentedApp -x .git -i lib/minifiedcopies and instrument all files inside myApp excluding .git which is not copied at all and lib/minified which is copied but won't be instrumented for coverage.
Collecting Coverage
When instrumented offline, files can be served
by node-coverage using as document root the instrumented path
by any other web server. Reports however should still be sent back to node-coverage either through XHR or form submit.
By default $$_l.submit sends an XHR POST request to /node-coverage-store containing the JSON report.
You can set up your server to redirect this request to node coverage or override the private method $$_l.__send. This method receives the coverage report as string.
node-coverage server accepts two types of POST request:
- XHR with
Content-type: application/jsonand coverage report as request body. - Form submit with
Content-type: application/x-www-form-urlencodedand coverage report as a string inside the fieldcoverage.
Unit Test
In order to run unit tests after cloning this repository you need to run
node instrument.js -tJSONP API
Once the server is started you can access the built-in adminitrative interface or use it's JSONP API to get reports as JSON objects and use them in your own tools.
You can target any page in the administrative interface adding a ?callback=myJsonPCallback GET parameter.
Empty space characters should be converted in %20.
Get the list of reports
http://localhost:8787/?callback=myCallbackThe returned JSON is an Array of objects containing
id: report nametime: creation timestampdate: creation date
Get the details of a report
http://localhost:8787/r/[id]?callback=myCallbackReplace [id] with the actual report's id.
The returned JSON has the following structure
globalstatementstotal: total number of lines,covered: number of exectuded statement,percentage: percentage of covered statements, float 0<>100,conditionstotal: total number of conditions,coveredTrue: number of conditions evaluated to true,coveredFalse: number of conditions evaluated to false,percentage: percentage of conditions evaluated both true and false,functionstotal: total number of functions,covered: number of functions that have been called (including empty functions), *percentage: percentage of functions calledfiles: map of single reports for every file. The key being the file name and the value being the file reportfunctions: history of all covered functions
By default files reports are sorted alphabetically by file name.
You can change the sorting criteria targeting
http://localhost:8787/r/[id]/sort/[what]/[how]?callback=myCallbackWhere
whatis eitherfilefor alphabetical sort orstatement,conditionorfunctionto sort according to the desired metric.howis eitherascordesc
Get the statistics of a report
http://localhost:8787/stat/[id]?callback=myCallbackReplace [id] with the actual report's id.
The returned JSON has the following structure
unused: number of unused statementsbyFile: object where the key is a file name and the value is the number of unused statementsbyPackage: group unused statements by "package" or folder.
Get a file report
http://localhost:8787/r/[id]/file/[fileName]?callback=myCallbackSlashes in fileName must be converted into +
The returned JSON contains
code: highlighted codesrc: array (one entry per line of code) where value are object withs: source linel: lineid of the instrumented functionc: list of conditions (array) *fns: object mapping a function id to the generated line of codestatementstotal: total number of lines,covered: number of exectuded statement,detail: coverage detail for every line, how many times that statement was called,percentage: percentage of covered statements, float 0<>100,conditionstotal: total number of conditions,coveredTrue: number of conditions evaluated to true,coveredFalse: number of conditions evaluated to false,detail: list of conditions that evaluated 'true' or 'false' and 'all' for both *percentage: percentage of conditions evaluated both true and false (100 if no conditions),functionstotal: total number of functions,covered: number of functions that have been called (including empty functions),percentage: percentage of functions called,detail: coverage detail of functions, how many times the function was called
Merge multiple reports
http://localhost:8787/merge/?report=[id]&report=[id]?callback=myCallbackWhere id is the report name. It's possible to merge more than two reports adding extra &report=[id]
The returned JSON has the same structure of a single report.
It's also possible to merge multiple reports from the command line
node merge.js -o destination_report.json report1.json report2.json [... reportN.json]Interpreters
node-coverage has a modular system for interpreting and instrumenting JavaScript files. This allows you to create an interpreter for any type of file.
The base interpreter is able to instrument standard JavaScript files, but you can create your own adding a module inside lib/interpreters with the following structure
exports.filter = {
files : /.*/, // a regular expression matching file names
content : /\/\!/ // a regular expression matching file content
};
exports.interpret = function (file, content, options) {}Filter object specifies which files are handled by the module.
filesis mandatory, it's a regular expression matching the file name, examples are/.*/for any file,/\.js$/for JavaScript filescontentis optional, it's a regular expression matching the file content. File content are checked against this expression only if their file name matchesfilter.files.
interpret is the function that instruments the code. It takes 3 parameters
fileFile namecontentFile contentoptionsCoverage optionsfunctionboolean, enable function coverageconditionboolean, enable condition coveragestaticInfoboolean, whether to include static information inside the instrumented codesubmitboolean, whether to include the submit function inside the instrumented code
this function must return an object containing
clientCodethe instrumented code, this is sent to the clientstaticInfoan object describing static information about the file
Proxy
node-coverage can also be used as an http proxy to instrument files hosted on a different machine.
node server.js --proxy -p 8000Start the instrumentation server in proxy mode. You can configure your browser to use an http proxy targeting localhost on port 8000
You can also enable or disable condition or function coverage using the same options of a standalone server or specify a differnt path where to store coverage reports.
node server.js --proxy --no-condition -r ~/reportsAt the moment it only support http, not https.
