1
// -*- test-case-name: nevow.test.test_javascript -*-
4
* JavaScript unit testing framework, modeled on xUnit.
9
// import Divmod.Inspect
10
// import Divmod.Runtime
14
* Return a suite which contains every test defined in C{testClass}. Assumes
15
* that if a method name starts with C{test_}, then it is a test.
17
Divmod.UnitTest.loadFromClass = function loadFromClass(testClass) {
19
var suite = Divmod.UnitTest.TestSuite();
20
var methods = Divmod.Inspect.methods(testClass);
21
for (var i = 0; i < methods.length; ++i) {
22
var name = methods[i];
23
// XXX - abstract startsWith
24
if (name.substr(0, prefix.length) == prefix) {
25
suite.addTest(testClass(name));
33
* Return C{true} is given value is a subclass of L{Divmod.UnitTest.TestCase},
36
Divmod.UnitTest.isTestCaseClass = function isTestCaseClass(klass) {
37
if (klass.subclassOf === undefined) {
40
return klass.subclassOf(Divmod.UnitTest.TestCase);
45
* Return a suite which contains every test defined in C{testModule}.
47
Divmod.UnitTest.loadFromModule = function loadFromModule(testModule) {
48
var suite = Divmod.UnitTest.TestSuite();
49
for (var name in testModule) {
50
if (Divmod.UnitTest.isTestCaseClass(testModule[name])) {
51
suite.addTest(Divmod.UnitTest.loadFromClass(testModule[name]));
60
* Raised to indicate that a test has failed.
62
Divmod.UnitTest.AssertionError = Divmod.Error.subclass('Divmod.UnitTest.AssertionError');
63
Divmod.UnitTest.AssertionError.methods(
64
function toString(self) {
65
return 'AssertionError: ' + self.message;
70
* Represents the results of a run of unit tests.
72
* @type testsRun: integer
73
* @ivar testsRun: The number of tests that have been run using this as the
76
* @type failures: Array of [L{TestCase}, L{Divmod.Error}] pairs
77
* @ivar failures: The assertion failures that have occurred in this test run,
78
* paired with the tests that generated them.
80
* @type successes: Array of L{TestCase}
81
* @ivar successes: A list of tests that succeeded.
83
* @type errors: Array of [L{TestCase}, L{Divmod.Error}] pairs
84
* @ivar errors: The errors that were raised by tests in this test run, paired
85
* with the tests that generated them.
87
Divmod.UnitTest.TestResult = Divmod.Class.subclass('Divmod.UnitTest.TestResult');
88
Divmod.UnitTest.TestResult.methods(
89
function __init__(self) {
98
* Called by C{TestCase.run} at the start of the test.
100
* @param test: The test that just started.
101
* @type test: L{Divmod.UnitTest.TestCase}
103
function startTest(self, test) {
109
* Called by C{TestCase.run} at the end of the test run.
111
* @param test: The test that just finished.
112
* @type test: L{Divmod.UnitTest.TestCase}
114
function stopTest(self, test) {
119
* Report an error that occurred while running the given test.
121
* @param test: The test that had an error.
122
* @type test: L{Divmod.UnitTest.TestCase}
124
* @param error: The error that occurred.
125
* @type error: Generally a L{Divmod.Error} instance.
127
function addError(self, test, error) {
128
self.errors.push([test, error]);
133
* Report a failed assertion that occurred while running the given test.
135
* @param test: The test with the failed assertion.
136
* @type test: L{Divmod.UnitTest.TestCase}
138
* @param failure: The failure that occurred.
139
* @type failure: A L{Divmod.UnitTest.AssertionError} instance.
141
function addFailure(self, test, failure) {
142
self.failures.push([test, failure]);
147
* Report that the given test succeeded.
149
* @param test: The test that succeeded.
150
* @type test: L{Divmod.UnitTest.TestCase}
152
function addSuccess(self, test) {
153
self.successes.push(test);
158
* Return a triple of (tests run, number of failures, number of errors)
160
function getSummary(self) {
161
return [self.testsRun, self.failures.length, self.errors.length];
166
* Return C{true} if there have been no failures or errors. Return C{false}
167
* if there have been.
169
function wasSuccessful(self) {
170
return self.failures.length == 0 && self.errors.length == 0;
175
Divmod.UnitTest.SubunitTestClient = Divmod.UnitTest.TestResult.subclass('Divmod.UnitTest.SubunitTestClient');
176
Divmod.UnitTest.SubunitTestClient.methods(
177
function _write(self, string) {
181
function _sendException(self, error) {
182
var f = Divmod.Defer.Failure(error);
183
self._write(f.toPrettyText(f.filteredParseStack()));
186
function addError(self, test, error) {
187
self._write("error: " + test.id() + " [");
188
self._sendException(error);
192
function addFailure(self, test, error) {
193
self._write("failure: " + test.id() + " [");
194
self._sendException(error);
198
function addSuccess(self, test) {
199
self._write('successful: ' + test.id());
202
function startTest(self, test) {
203
self._write('test: ' + test.id());
209
* Represents a collection of tests. Implements the Composite pattern.
211
Divmod.UnitTest.TestSuite = Divmod.Class.subclass('Divmod.UnitTest.TestSuite');
212
Divmod.UnitTest.TestSuite.methods(
213
function __init__(self, /* optional */ tests) {
215
if (tests != undefined) {
216
self.addTests(tests);
222
* Add the given test to the suite.
224
* @param test: The test to add.
225
* @type test: L{Divmod.UnitTest.TestCase} or L{Divmod.UnitTest.TestSuite}
227
function addTest(self, test) {
228
self.tests.push(test);
233
* Add the given tests to the suite.
235
* @param tests: An array of tests to add.
236
* @type tests: [L{Divmod.UnitTest.TestCase} or L{Divmod.UnitTest.TestSuite}]
238
function addTests(self, tests) {
239
for (var i = 0; i < tests.length; ++i) {
240
self.addTest(tests[i]);
246
* Return the number of actual tests contained in this suite.
248
function countTestCases(self) {
250
self.visit(function (test) { total += test.countTestCases(); });
256
* Visit each test case in this suite with the given visitor function.
258
function visit(self, visitor) {
259
for (var i = 0; i < self.tests.length; ++i) {
260
self.tests[i].visit(visitor);
266
* Run all of the tests in the suite.
268
function run(self, result) {
269
self.visit(function (test) { test.run(result); });
275
* I represent a single unit test.
277
Divmod.UnitTest.TestCase = Divmod.Class.subclass('Divmod.UnitTest.TestCase');
278
Divmod.UnitTest.TestCase.methods(
282
* @type methodName: string
283
* @param methodName: The name of a method on this object that contains
286
function __init__(self, methodName) {
287
self._methodName = methodName;
292
* Return a string which identifies this test.
295
return self.__class__.__name__ + '.' + self._methodName;
300
* Count the number of test cases in this test. Always 1, because an
301
* instance represents a single test.
303
function countTestCases(self) {
309
* Visit this test case.
311
* @param visitor: A callable which takes one argument (a test case).
313
function visit(self, visitor) {
319
* Fail the test. Equivalent to an invalid assertion.
322
* @param reason: Why the test is being failed.
323
* @throw: Divmod.UnitTest.AssertionError
325
function fail(self, reason) {
326
throw Divmod.UnitTest.AssertionError(reason);
331
* Assert that the given expression evalutates to true.
333
* @type expression: boolean
334
* @param expression: The thing we are asserting.
336
* @type message: text
337
* @param message: An optional parameter, explaining what the assertion
340
function assert(self, expression, /* optional */ message) {
348
* Compare C{a} and C{b} using the provided predicate.
350
* @type predicate: A callable that accepts two parameters.
351
* @param predicate: Returns either C{true} or C{false}.
353
* @type description: text
354
* @param description: Describes the inverse of the comparison. This is
355
* used in the L{AssertionError} if the comparison
359
* @param a: The thing to be compared with C{b}. Passed as the first
360
* parameter to C{predicate}.
363
* @param b: The thing to be compared with C{a}. Passed as the second
364
* parameter to C{predicate}.
366
* @type message: text
367
* @param message: An optional message to be included in the raised
370
* @raises L{Divmod.UnitTest.AssertionError} if C{predicate} returns
373
function compare(self, predicate, description, a, b,
374
/* optional */ message) {
375
var repr = Divmod.UnitTest.repr;
376
if (!predicate(a, b)) {
377
msg = repr(a) + " " + description + " " + repr(b);
378
if (message != null) {
379
msg += ': ' + message;
387
* Assert that C{a} and C{b} are equal. Recurses into arrays and dicts.
389
function assertArraysEqual(self, a, b, /* optional */ message) {
390
self.compare(Divmod.arraysEqual, '!=', a, b, message);
395
* Assert that C{a} and C{b} are identical.
397
function assertIdentical(self, a, b, /* optional */ message) {
398
self.compare(function (x, y) { return x === y; },
399
'!==', a, b, message);
404
* Assert that C{callable} throws C{expectedError}
406
* @param expectedError: The error type (class or prototype) which is
407
* expected to be thrown.
409
* @param callable: A callable which is expected to throw C{expectedError}.
411
* @param ...: Optional positional arguments passed to C{callable}.
413
* @throw AssertionError: Thrown if the callable doesn't throw
414
* C{expectedError}. This could be because it threw a different error or
415
* because it didn't throw any errors.
417
* @return: The exception that was raised by callable.
419
function assertThrows(self, expectedError, callable /*... */) {
421
var args = Array.prototype.slice.call(arguments, 3);
423
callable.apply(null, args);
426
self.assert(e instanceof expectedError,
427
"Wrong error type thrown: " + e);
429
self.assert(threw != null, "Callable threw no error");
435
* Override me to provide code to set up a unit test. This method is called
436
* before the test method.
438
* L{setUp} is most useful when a subclass contains many test methods which
439
* require a common base configuration. L{tearDown} is the complement of
442
function setUp(self) {
447
* Override me to provide code to clean up a unit test. This method is called
448
* after the test method.
450
* L{tearDown} is at its most useful when used to clean up resources that are
451
* initialized/modified by L{setUp} or by the test method.
453
function tearDown(self) {
458
* Actually run this test.
460
function run(self, result) {
462
result.startTest(self);
464
// XXX: This probably isn't the best place to put this, but it's the
465
// only place for the time being; see #2806 for the proper way to deal
467
Divmod.Runtime.initRuntime();
472
result.addError(self, e);
476
self[self._methodName]();
478
if (e instanceof Divmod.UnitTest.AssertionError) {
479
result.addFailure(self, e);
481
result.addError(self, e);
488
result.addError(self, e);
492
result.addSuccess(self);
494
result.stopTest(self);
500
* Return a nicely formatted summary from the given L{TestResult}.
502
Divmod.UnitTest.formatSummary = function formatSummary(result) {
504
if (result.wasSuccessful()) {
509
summary += "(tests=" + result.testsRun;
510
if (result.errors.length > 0) {
511
summary += ", errors=" + result.errors.length
513
if (result.failures.length > 0) {
514
summary += ", failures=" + result.failures.length;
523
* Return a formatted string containing all the errors and failures in a result
525
* @param result: A test result.
526
* @type result: L{Divmod.UnitTest.TestResult}
528
Divmod.UnitTest.formatErrors = function formatErrors(result) {
530
for (var i = 0; i < result.errors.length; ++i) {
531
format += Divmod.UnitTest.formatError('ERROR',
533
result.errors[i][1]);
535
for (var i = 0; i < result.failures.length; ++i) {
536
format += Divmod.UnitTest.formatError('FAILURE',
537
result.failures[i][0],
538
result.failures[i][1]);
546
* Return a formatting string showing the failure/error that occurred in a test.
548
* @param test: A test which had a failure or error.
549
* @type test: L{Divmod.UnitTest.TestCase}
551
* @param error: An error or failure which occurred in the test.
552
* @type error: L{Divmod.Error}
554
Divmod.UnitTest.formatError = function formatError(kind, test, error) {
555
var f = Divmod.Defer.Failure(error);
556
var ret = '[' + kind + '] ' + test.id() + ': ' + error.message + '\n';
557
ret += f.toPrettyText(f.filteredParseStack()) + '\n';
564
* Run the given test, printing the summary of results and any errors. If run
565
* inside a web browser, it will try to print these things to the printer, so
566
* don't use this in a web browser.
568
* @param test: The test to run.
569
* @type test: L{Divmod.UnitTest.TestCase} or L{Divmod.UnitTest.TestSuite}
571
Divmod.UnitTest.run = function run(test) {
572
var result = Divmod.UnitTest.TestResult()
574
print(Divmod.UnitTest.formatErrors(result));
575
print(Divmod.UnitTest.formatSummary(result));
579
Divmod.UnitTest.runRemote = function runRemote(test) {
580
var result = Divmod.UnitTest.SubunitTestClient();
586
* Return a string representation of an arbitrary value, similar to
587
* Python's builtin repr() function.
589
Divmod.UnitTest.repr = function repr(value) {
590
// We can't call methods on undefined or null.
591
if (value === undefined) {
593
} else if (value === null) {
595
} else if (typeof value === 'string') {
596
return '"' + value + '"';
597
} else if (typeof value === 'number') {
599
} else if (value.toSource !== undefined) {
600
return value.toSource();
601
} else if (value.toString !== undefined) {
602
return value.toString();