TDDforJS
A Test Driven Development framework for Javascript. TDD aims to provide a very simple test running environment, that mimics jUnit. It doesn't provide any sort of assertion framework, and it's not going to fold your clothes either. Just a few nice conventions to make testing JS a bit easier.
Requirements
node and npm
Setup
npm install tddforjs
$(npm bin)/tddforjs
- Set the appropritate values in
config/tddforjs.json
$(npm bin)/tddforjs
It's really that simple.
Config
TDD uses config-tools to look for a config/tddforjs.json
file within your
project root.
The following is a default config file:
"reporting": "mode":"cli" "available": "cli" "junit" "testng" "base":"../reports" "output": "types": "junit":true "testng":false "src": "base":"../src" "js":"./js" "names": ".*\\.js" "test": "base":"../test" "integrations":"./integrations" "units":"./units" "names": "units":".*\\.js" "integrations":".*\\.js"
Convention
By default, TDD assumes that each file under src/js
will have an accompanying
unit test under the test/units
directory.
A default project structure looks like this:
$ProjectRoot/
|
|
|____src/
| |
| |____js/
| |
| |__MyFile.js
|
|
|____test/
|
|____units/
| |
| |__MyFile.js
|
|
|____integrations/
TDD will run tests in units/MyFile.js
against src/MyFile.js
.
All suites found in integrations/
will be run as well.
Sample Test Suite
Here is a sample test suite. The inspiration comes from jUnit 4.x, so the following holds true:
- if a function marked
before
exists within your Test Suite, it will be executed before any test - if a function marked
after
exists within your Test Suite, it will be executed after any test - any function prefixed with
//Test
is considered a test. - Errors with the term
assert
in their constructor show as a failure. - Errors not an instanceof Error show as a failure.
- Errors that are an instance of Error show as an error.
var assert = ;var factory;{ factory = ;}{} //Test{ {}; if!factory instanceof ImportResolver throw "ImportResolver wasn't created."; }//Test{ {}; if!factory instanceof TDDforJSEvaluator throw "TDDforJSEvaluator wasn't created."; }//Test{ {}; if!factory instanceof TestSuite assert; }//Test{ {}; if!factory instanceof TestSuites assert; }//Test{ {}; if!factory instanceof SuiteFileResolver assert; }
And here's the source file:
/** * @constructor * @param * @param * @param */{ var instance = this; /** * @param * @param * @return */ this{ return fsModule pathModule sourceBase testBase; }; /** @return */ this{ return ; }; /** * @param * @param * @param * @param * @returns */ this{ return "__$$__" className hostname id source ; }; /** * @param * @param * @param * @param * @param * @returns */ this{ return files instance hostname fileResolver importResolver pathModule evaluator extraSourceFn ; }; /** * @param * @returns */ this{ return fsModule pathModule baseDir ; };}
Importing
TDD allows your test suites to import any javascript file for the duration of your test. The following paths are searched for a match in order:
test/
src/
To define imports, you specify comments at the top of your suite like this:
//import foo//import lib/jsmockito{}//continue testing...
Errors in Source
Because TDD is declarative in the sense that you follow a convention to define test cases, TDD can report the number of test cases defined in a suite even when they can't be run due to an uncaught error in your source, suite, or any import.
Let's say we have the following source:
//import foothrow 5;
And we're testing it with a unit test:
//Test{}
We would report 1 test and 1 failure for our unit test, despite the fact that there's clearly an error in our source file when the unit test is run.
This feature extends to suites and imported files.
Reporting
TDD aims to provide support for three types of reports:
- CLI
- jUnit XML
- TestNG XML
The two latter types would enable integration with a CI server like Hudson or Jenkins.
Here's what a sample report looks like from the CLI:
========================================
UNIT TEST REPORT
========================================
Suites : 6
Tests : 50
Failures : 0
Errors : 0
========================================
INTEGRATION TEST REPORT
========================================
Suites : 1
Tests : 0
Failures : 0
Errors : 0
Here's what a junit report looks like for hudson and jenkins:
TDD only reports errors and failures, so if we were to do something in our sample suite that would fail a test, the output would look like this for the CLI:
========================================
UNIT TEST REPORT
========================================
Suites : 6
Tests : 50
Failures : 1
Errors : 0
Suite: AppFactory
Tests : 5
Failures : 1
Errors : 0
Case : ImportResolver_should_be_creatable
Failure : unknown
: boo
========================================
INTEGRATION TEST REPORT
========================================
Suites : 1
Tests : 0
Failures : 0
Errors : 0
And in the JUnit report:
Console Logging
Console logging can come in handy for debugging purposes. It can also be very difficult to decipher it's output without any context.
TDD overrides console and places the messages in the reports.
Let's add a console.log
statement to a test case:
//Test{ {}; if!factory instanceof ImportResolver throw "ImportResolver wasn't created."; console}
Here's what the CLI report shows:
========================================
UNIT TEST REPORT
========================================
Suites : 6
Tests : 50
Failures : 0
Errors : 0
Suite: AppFactory
Tests : 5
Failures : 0
Errors : 0
Stdout :
console.log(calling console.log , with some arguments , 5 , 6 , 7)
========================================
INTEGRATION TEST REPORT
========================================
Suites : 1
Tests : 0
Failures : 0
Errors : 0
Here's what the JUnit report shows:
console.log(calling console.log , with some arguments , 5 , 6 , 7);