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.
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 = true
has 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 = false
won't increase condition coverage because the second condition (wheter b is true or not) is never checked by the language.
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 install
Unit tests run on Nodeunit.
The administrative interface uses for "Stats & Graph" page
- jQuery
- Highcharts charting library written in JavaScript
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:8080
and 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:8787
It's also possible to specify a report name from the submit function
$$_l.submit("myTestCaseReport")
-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 --function
or
node server.js --no-condition
to disable condition coverage.
You can exclude some files or folders using
node server.js -i lib/minified -i lib/jquery.js
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/myInstrumentedApp
You can then run the server with
node server.js -d /var/www/myInstrumentedApp
-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/myInstrumentedApp
or
node instrument.js --no-condition /var/www/myApp /var/www/myInstrumentedApp
to 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.js
The output is sent to standard input.
The command
node instrument /var/www/myApp /var/www/myInstrumentedApp -x .git -i lib/minified
copies 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.
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.
In order to run unit tests after cloning this repository you need to run
node instrument.js -t
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.
http://localhost:8787/?callback=myCallback
The returned JSON is an Array of objects containing
id: report nametime: creation timestampdate: creation date
http://localhost:8787/r/[id]?callback=myCallback
Replace [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 called
files: 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=myCallback
Where
whatis eitherfilefor alphabetical sort orstatement,conditionorfunctionto sort according to the desired metric.howis eitherascordesc
http://localhost:8787/stat/[id]?callback=myCallback
Replace [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.
http://localhost:8787/r/[id]/file/[fileName]?callback=myCallback
Slashes 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 code
statementstotal: 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 bothpercentage: 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
http://localhost:8787/merge/?report=[id]&report=[id]?callback=myCallback
Where 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]
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
node-coverage can also be used as an http proxy to instrument files hosted on a different machine.
node server.js --proxy -p 8000
Start 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 ~/reports
At the moment it only support http, not https.
