This file provides a wrapper for two extant JsUnit implementations:
This allows tests to be written which will run unchanged against either of the JsUnit implementations. There is a partial effort by the authors of those two projects (they are by no means hostile to each other), but that effort has not yet come to fruition, and in any event there are enough other issues with both of them to merit some separation, at least for now.
This file also provides its own built-in implementation, if neither JsUnit is available. It works in either a shell or a browser.
[ NOTE: there is also ECMAUnit, at http://kupu.oscom.org/download/ with a BSD license. It is command-line only. It has better implemented internals than Schaible JsUnit. We have not looked at it more closely at this time. ]
The wrapper determines which JsUnit implementation is in use through object detection.
It defines a javascript global called jum which represents the test environment. Methods on this object are used for assertions and any IO.
The sequence of script file loading should be as follows:
In the case of Hieatt JsUnit, the above scripts would be loaded via 'script' elements in an HTML page. In the case of Schaible JsUnit, the above scripts would be loaded on a javascript interpreter command line (such as via a "-f" option), or programmatically (for example by using a global load() function supplied by the environment).
We attempt to be well-behaved with respect to the underlying JsUnit implementation which is in use, so it should be possible to mix and match tests written to this API with ones using the implementation-specific API.
Some of the features of this wrapper are:
For Hieatt JsUnit, if exposeTestFunctionNames is defined, we do nothing and rely on that. Otherwise we supply an implementation for that function. Either way, exposeTestFunctionNames is defined, so that Hieatt JsUnit's own attempts at reflection are never utilitized.
For Schaible JsUnit, if AllTests.suite is defined, we do nothing and rely on that. Otherwise we supply a definition.
If the jum.TEST_FUNCTION_NAMES is set, it is assumed to be a set of global function names of tests. If it is set, we do no reflection.
JUnit has a confusing set of terminology which JsUnit implementations retain (and are not to blame for). In JUnit
Schaible JsUnit mimics JUnit fairly closely, requiring the programming writing tests to subclass TestCase (using ECMAScript prototypes). There is also a TestSuite "class". In Schaible JsUnit, the TestSuite class does not extend TestCase. (There is some unnecessary confusion in the implementation concerning new() and constructors, but the idea is preserved.)
Schaible JsUnit does no reflection at the global level. It does do reflection-based discovery of test methods given a TestCase instance.
Hieatt JsUnit does not retain this terminology. It simply assumes there are any number of functions defined at the global level in some html page. A test "suite" is a collection of html pages.
Hieatt JsUnit attempts to use reflection to discover all test functions defined in an html page.
In our implementation, we introduce the notion of test "group", which is a collection of test functions. It is more akin to the JUnit "TestCase" in implmenetation, though given the name "TestCase", that is more often thought of as a single test.
We also have a single-level containment hierarchy, a collection of all test groups.
For us, a test "name" is systematically related to a test function name (in both directions).
Thus we draw a distinction between:
In Schaible JsUnit, we create a TestCase for each group. We then create a TestSuite which contains just that single TestCase.
There is a method jum.debug
for debug output to be called from your tests.
You may or may not want any debug output from your library under test to be handled the same way. If so, it is up to you to override your own debug output functionality in your library under test, to call jum.debug
when jum is defined.
If you want to be able to do deep comparison in assertEquals, an uneval implementation must be available. That means you need either burst.Lang.uneval, or you must be on a Mozilla javascript (which has Object.toSource()).
Hieatt assertTrue and assertFalse require that their argument is typeof boolean. Schaible assertTrue and assertFalse do an eval.
Neither implementation does deep equality in their assertEquals. That means it is difficult to test results against an Array or simple object like {a: 1, b: 2}.
Both are weak at reflection.
Some issues with both:
Some issues with Hieatt JsUnit:
Some issues with Schaible JsUnit:
Licensed under the Academic Free License 1.2 http://www.opensource.org/licenses/academic.php
Functions | |
public void | debug (String line) |
display the String as debug output (if enabled by the test environment) | |
public void | info (String line) |
display the String as info output (if enabled by the test environment) | |
public void | warn (String line) |
display the String as warn output (if enabled by the test environment) | |
public void | assertTrue (String message, Object obj) |
Assert that eval(obj) tests as true. | |
public void | assertTrue (Object obj) |
Variant for 1 arg. | |
public void | assertFalse (String message, Object obj) |
Assert that eval(obj) tests as false. | |
public void | assertFalse (Object obj) |
Variant for 1 arg. | |
public void | assertEquals (String message, Object expected, Object actual) |
Assert that expected and actual are equal. | |
public void | assertEquals (Object expected, Object actual) |
Variant for 2 args. | |
public void | runAll () |
Run all tests. | |
public void | untested (String funcname) |
Report that the specified test is not being tested (this is different from the test being tried and failed). | |
public Object | pause (String funcname, String id, String desc) |
Indicate that this test is not over and will be continued asynchronously. | |
public void | resume (String funcname, String id, Function func) |
Ask the test manager to resume execution for the named test. | |
public Boolean | waitFor (String other_funcname, String other_id, String funcname, String id, Function func) |
Wait for the specified (other) test to have its resume done, then call this one. | |
public void | init (Object scopeobj) |
Initializes the test runner. | |
public Boolean | isFailureException (Object e) |
Return true if and only if the object is an exception indicating a test failure (vs. | |
public Boolean | areTestsChosen () |
Return true if and only if the tests are (somehow) already chosen, so that the wrapper should not try to determine and set them. | |
public void | setTests (Object alltests) |
Set all the tests available. | |
public void | reportAsync () |
Should be called after all (synchronous) tests are run. | |
Variables | |
public RegExp | TEST_FUNCTION_REGEXP |
By default match as 'test_' + groupname + '_' + testname. |
|
Return true if and only if the tests are (somehow) already chosen, so that the wrapper should not try to determine and set them. protected abstract. |
|
Assert that expected and actual are equal. Out of the box, Hieatt does ===. Schaible uses == except when expected is a Regexp, and actual is a string, in which case it tests actual.match(expected). Neither attempts a "deep" equality test, which makes comparison to Array or Object difficult. If the function burst.Lang.uneval is not defined, we do nothing about all this, and just call the underlying function. If the function burst.Lang.uneval is available, it is applied to expected or actual if they have typeof 'object'. In this way, a basic "deep" comparison is possible. |
|
Assert that eval(obj) tests as false. Overloaded for 1 or 2 args (message is optional) |
|
Assert that eval(obj) tests as true. Overloaded for 1 or 2 args (message is optional). (We wrap Hieatt assertTrue and assertFalse to accomplish this semantic; natively it requires obj to be Boolean.) |
|
Initializes the test runner. Concrete method in the base class. Must be called after all tests scripts are loaded. |
|
Return true if and only if the object is an exception indicating a test failure (vs. a test error). protected abstract. |
|
Indicate that this test is not over and will be continued asynchronously.
|
|
Should be called after all (synchronous) tests are run. It first waits for completion of any outstanding asyncs (pause but no matching resume) for up to jum.DEFAULT_TIMEOUT_MILLIS. |
|
Ask the test manager to resume execution for the named test.
|
|
Run all tests. Only has to implemented in non-gui test managers. |
|
Set all the tests available. protected abstract.
|
|
Report that the specified test is not being tested (this is different from the test being tried and failed). By default, this is implemented as a particular message to jum.warn(), since this is not a concept supported in the JsUnit implementations (or JUnit, for that matter).
|
|
Wait for the specified (other) test to have its resume done, then call this one. Returns true if other is already done. Throws if other is never heard of (no previous pause call). |