| OLD | NEW |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * A library for writing dart unit tests. | 6 * A library for writing dart unit tests. |
| 7 * | 7 * |
| 8 * To import this library, specify the relative path to | 8 * To import this library, specify the relative path to |
| 9 * lib/unittest/unittest.dart. | 9 * lib/unittest/unittest.dart. |
| 10 * | 10 * |
| 11 * ##Concepts## | 11 * ##Concepts## |
| 12 * | 12 * |
| 13 * * Tests: Tests are specified via the top-level function [test], they can be | 13 * * Tests: Tests are specified via the top-level function [test], they can be |
| 14 * organized together using [group]. | 14 * organized together using [group]. |
| 15 * * Checks: Test expectations can be specified via [expect] (see methods in | 15 * * Checks: Test expectations can be specified via [expect] |
| 16 * [Expectation]), [expectThrow], or using assertions with the [Expect] | 16 * * Matchers: [expect] assertions are written declaratively using [Matcher]s |
| 17 * class. | |
| 18 * * Configuration: The framework can be adapted by calling [configure] with a | 17 * * Configuration: The framework can be adapted by calling [configure] with a |
| 19 * [Configuration]. Common configurations can be found in this package | 18 * [Configuration]. Common configurations can be found in this package |
| 20 * under: 'dom\_config.dart', 'html\_config.dart', and 'vm\_config.dart'. | 19 * under: 'dom\_config.dart' (deprecated), 'html\_config.dart' (for running |
| 20 * tests compiled to Javascript in a browser), and 'vm\_config.dart' (for |
| 21 * running native Dart tests on the VM). |
| 21 * | 22 * |
| 22 * ##Examples## | 23 * ##Examples## |
| 23 * | 24 * |
| 24 * A trivial test: | 25 * A trivial test: |
| 25 * | 26 * |
| 26 * #import('path-to-dart/lib/unittest/unitest.dart'); | 27 * #import('path-to-dart/lib/unittest/unitest.dart'); |
| 27 * main() { | 28 * main() { |
| 28 * test('this is a test', () { | 29 * test('this is a test', () { |
| 29 * int x = 2 + 3; | 30 * int x = 2 + 3; |
| 30 * expect(x).equals(5); | 31 * expect(x, equals(5)); |
| 31 * }); | 32 * }); |
| 32 * } | 33 * } |
| 33 * | 34 * |
| 34 * Multiple tests: | 35 * Multiple tests: |
| 35 * | 36 * |
| 36 * #import('path-to-dart/lib/unittest/unitest.dart'); | 37 * #import('path-to-dart/lib/unittest/unitest.dart'); |
| 37 * main() { | 38 * main() { |
| 38 * test('this is a test', () { | 39 * test('this is a test', () { |
| 39 * int x = 2 + 3; | 40 * int x = 2 + 3; |
| 40 * expect(x).equals(5); | 41 * expect(x, equals(5)); |
| 41 * }); | 42 * }); |
| 42 * test('this is another test', () { | 43 * test('this is another test', () { |
| 43 * int x = 2 + 3; | 44 * int x = 2 + 3; |
| 44 * expect(x).equals(5); | 45 * expect(x, equals(5)); |
| 45 * }); | 46 * }); |
| 46 * } | 47 * } |
| 47 * | 48 * |
| 48 * Multiple tests, grouped by category: | 49 * Multiple tests, grouped by category: |
| 49 * | 50 * |
| 50 * #import('path-to-dart/lib/unittest/unitest.dart'); | 51 * #import('path-to-dart/lib/unittest/unitest.dart'); |
| 51 * main() { | 52 * main() { |
| 52 * group('group A', () { | 53 * group('group A', () { |
| 53 * test('test A.1', () { | 54 * test('test A.1', () { |
| 54 * int x = 2 + 3; | 55 * int x = 2 + 3; |
| 55 * expect(x).equals(5); | 56 * expect(x, equals(5)); |
| 56 * }); | 57 * }); |
| 57 * test('test A.2', () { | 58 * test('test A.2', () { |
| 58 * int x = 2 + 3; | 59 * int x = 2 + 3; |
| 59 * expect(x).equals(5); | 60 * expect(x, equals(5)); |
| 60 * }); | 61 * }); |
| 61 * }); | 62 * }); |
| 62 * group('group B', () { | 63 * group('group B', () { |
| 63 * test('this B.1', () { | 64 * test('this B.1', () { |
| 64 * int x = 2 + 3; | 65 * int x = 2 + 3; |
| 65 * expect(x).equals(5); | 66 * expect(x, equals(5)); |
| 66 * }); | 67 * }); |
| 67 * }); | 68 * }); |
| 68 * } | 69 * } |
| 69 * | 70 * |
| 70 * Asynchronous tests: if callbacks expect between 0 and 2 positional arguments. | 71 * Asynchronous tests: if callbacks expect between 0 and 2 positional arguments, |
| 72 * depending on the suffix of expectAsyncX(). expectAsyncX() will wrap a |
| 73 * function into a new callback and will not consider the test complete until |
| 74 * that callback is run. A count argument can be provided to specify the number |
| 75 * of times the callback should be called (the default is 1). |
| 71 * | 76 * |
| 72 * #import('path-to-dart/lib/unittest/unitest.dart'); | 77 * #import('path-to-dart/lib/unittest/unitest.dart'); |
| 73 * #import('dart:dom_deprecated'); | 78 * #import('dart:dom_deprecated'); |
| 74 * main() { | 79 * main() { |
| 75 * test('calllback is executed once', () { | 80 * test('calllback is executed once', () { |
| 76 * // wrap the callback of an asynchronous call with [expectAsync0] if | 81 * // wrap the callback of an asynchronous call with [expectAsync0] if |
| 77 * // the callback takes 0 arguments... | 82 * // the callback takes 0 arguments... |
| 78 * window.setTimeout(expectAsync0(() { | 83 * window.setTimeout(expectAsync0(() { |
| 79 * int x = 2 + 3; | 84 * int x = 2 + 3; |
| 80 * expect(x).equals(5); | 85 * expect(x, equals(5)); |
| 81 * }), 0); | 86 * }), 0); |
| 82 * }); | 87 * }); |
| 83 * | 88 * |
| 84 * test('calllback is executed twice', () { | 89 * test('calllback is executed twice', () { |
| 85 * var callback = expectAsync0(() { | 90 * var callback = expectAsync0(() { |
| 86 * int x = 2 + 3; | 91 * int x = 2 + 3; |
| 87 * expect(x).equals(5); | 92 * expect(x, equals(5)); |
| 88 * }, count: 2); // <-- we can indicate multiplicity to [expectAsync0] | 93 * }, count: 2); // <-- we can indicate multiplicity to [expectAsync0] |
| 89 * window.setTimeout(callback, 0); | 94 * window.setTimeout(callback, 0); |
| 90 * window.setTimeout(callback, 0); | 95 * window.setTimeout(callback, 0); |
| 91 * }); | 96 * }); |
| 92 * } | 97 * } |
| 93 * | 98 * |
| 99 * expectAsyncX() will wrap the callback code in a try/catch handler to handle |
| 100 * exceptions (treated as test failures). There may be times when the number of |
| 101 * times a callback should be called is non-deterministic. In this case a dummy |
| 102 * callback can be created with expectAsync0((){}) and this can be called from |
| 103 * the real callback when it is finally complete. In this case the body of the |
| 104 * callback should be protected within a call to guardAsync(); this will ensure |
| 105 * that exceptions are properly handled. |
| 106 * |
| 94 * Note: due to some language limitations we have to use different functions | 107 * Note: due to some language limitations we have to use different functions |
| 95 * depending on the number of positional arguments of the callback. In the | 108 * depending on the number of positional arguments of the callback. In the |
| 96 * future, we plan to expose a single `expectAsync` function that can be used | 109 * future, we plan to expose a single `expectAsync` function that can be used |
| 97 * regardless of the number of positional arguments. This requires new langauge | 110 * regardless of the number of positional arguments. This requires new langauge |
| 98 * features or fixes to the current spec (e.g. see | 111 * features or fixes to the current spec (e.g. see |
| 99 * [Issue 2706](http://dartbug.com/2706)). | 112 * [Issue 2706](http://dartbug.com/2706)). |
| 100 * | 113 * |
| 101 * Meanwhile, we plan to add this alternative API for callbacks of more than 2 | 114 * Meanwhile, we plan to add this alternative API for callbacks of more than 2 |
| 102 * arguments or that take named parameters. (this is not implemented yet, | 115 * arguments or that take named parameters. (this is not implemented yet, |
| 103 * but will be coming here soon). | 116 * but will be coming here soon). |
| 104 * | 117 * |
| 105 * #import('path-to-dart/lib/unittest/unitest.dart'); | 118 * #import('path-to-dart/lib/unittest/unitest.dart'); |
| 106 * #import('dart:dom_deprecated'); | 119 * #import('dart:dom_deprecated'); |
| 107 * main() { | 120 * main() { |
| 108 * test('calllback is executed', () { | 121 * test('calllback is executed', () { |
| 109 * // indicate ahead of time that an async callback is expected. | 122 * // indicate ahead of time that an async callback is expected. |
| 110 * var async = startAsync(); | 123 * var async = startAsync(); |
| 111 * window.setTimeout(() { | 124 * window.setTimeout(() { |
| 112 * // Guard the body of the callback, so errors are propagated | 125 * // Guard the body of the callback, so errors are propagated |
| 113 * // correctly | 126 * // correctly |
| 114 * guardAsync(() { | 127 * guardAsync(() { |
| 115 * int x = 2 + 3; | 128 * int x = 2 + 3; |
| 116 * expect(x).equals(5); | 129 * expect(x, equals(5)); |
| 117 * }); | 130 * }); |
| 118 * // indicate that the asynchronous callback was invoked. | 131 * // indicate that the asynchronous callback was invoked. |
| 119 * async.complete(); | 132 * async.complete(); |
| 120 * }), 0); | 133 * }), 0); |
| 121 * }); | 134 * }); |
| 122 * | 135 * |
| 123 */ | 136 */ |
| 124 #library('unittest'); | 137 #library('unittest'); |
| 125 | 138 |
| 126 #import('dart:isolate'); | 139 #import('dart:isolate'); |
| 127 | 140 |
| 141 #source('collection_matchers.dart'); |
| 128 #source('config.dart'); | 142 #source('config.dart'); |
| 129 #source('expectation.dart'); | 143 #source('core_matchers.dart'); |
| 144 #source('description.dart'); |
| 145 #source('expect.dart'); |
| 146 #source('interfaces.dart'); |
| 147 #source('map_matchers.dart'); |
| 148 #source('matcher.dart'); |
| 149 #source('numeric_matchers.dart'); |
| 150 #source('operator_matchers.dart'); |
| 151 #source('string_matchers.dart'); |
| 130 #source('test_case.dart'); | 152 #source('test_case.dart'); |
| 131 | 153 |
| 132 /** [Configuration] used by the unittest library. */ | 154 /** [Configuration] used by the unittest library. */ |
| 133 Configuration _config = null; | 155 Configuration _config = null; |
| 134 | 156 |
| 135 /** Set the [Configuration] used by the unittest library. */ | 157 /** Set the [Configuration] used by the unittest library. */ |
| 136 void configure(Configuration config) { | 158 void configure(Configuration config) { |
| 137 _config = config; | 159 _config = config; |
| 138 } | 160 } |
| 139 | 161 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 172 int _state = _UNINITIALIZED; | 194 int _state = _UNINITIALIZED; |
| 173 String _uncaughtErrorMessage = null; | 195 String _uncaughtErrorMessage = null; |
| 174 | 196 |
| 175 final _PASS = 'pass'; | 197 final _PASS = 'pass'; |
| 176 final _FAIL = 'fail'; | 198 final _FAIL = 'fail'; |
| 177 final _ERROR = 'error'; | 199 final _ERROR = 'error'; |
| 178 | 200 |
| 179 /** If set, then all other test cases will be ignored. */ | 201 /** If set, then all other test cases will be ignored. */ |
| 180 TestCase _soloTest; | 202 TestCase _soloTest; |
| 181 | 203 |
| 182 /** Creates an expectation for the given value. */ | |
| 183 Expectation expect(value) => new Expectation(value); | |
| 184 | |
| 185 /** | 204 /** |
| 186 * Evaluates the [function] and validates that it throws an exception. If | 205 * (Deprecated) Evaluates the [function] and validates that it throws an |
| 187 * [callback] is provided, then it will be invoked with the thrown exception. | 206 * exception. If [callback] is provided, then it will be invoked with the |
| 188 * The callback may do any validation it wants. In addition, if it returns | 207 * thrown exception. The callback may do any validation it wants. In addition, |
| 189 * `false`, that also indicates an expectation failure. | 208 * if it returns `false`, that also indicates an expectation failure. |
| 190 */ | 209 */ |
| 191 void expectThrow(function, [bool callback(exception)]) { | 210 void expectThrow(function, [bool callback(exception)]) { |
| 192 bool threw = false; | 211 bool threw = false; |
| 193 try { | 212 try { |
| 194 function(); | 213 function(); |
| 195 } catch (var e) { | 214 } catch (var e) { |
| 196 threw = true; | 215 threw = true; |
| 197 | 216 |
| 198 // Also let the callback look at it. | 217 // Also let the callback look at it. |
| 199 if (callback != null) { | 218 if (callback != null) { |
| (...skipping 15 matching lines...) Expand all Loading... |
| 215 * description will include the descriptions of any surrounding group() | 234 * description will include the descriptions of any surrounding group() |
| 216 * calls. | 235 * calls. |
| 217 */ | 236 */ |
| 218 void test(String spec, TestFunction body) { | 237 void test(String spec, TestFunction body) { |
| 219 ensureInitialized(); | 238 ensureInitialized(); |
| 220 | 239 |
| 221 _tests.add(new TestCase(_tests.length + 1, _fullSpec(spec), body, 0)); | 240 _tests.add(new TestCase(_tests.length + 1, _fullSpec(spec), body, 0)); |
| 222 } | 241 } |
| 223 | 242 |
| 224 /** | 243 /** |
| 225 * Creates a new async test case with the given description and body. The | 244 * (Deprecated) Creates a new async test case with the given description |
| 226 * description will include the descriptions of any surrounding group() | 245 * and body. The description will include the descriptions of any surrounding |
| 227 * calls. | 246 * group() calls. |
| 228 */ | 247 */ |
| 229 // TODO(sigmund): deprecate this API | 248 // TODO(sigmund): deprecate this API |
| 230 void asyncTest(String spec, int callbacks, TestFunction body) { | 249 void asyncTest(String spec, int callbacks, TestFunction body) { |
| 231 ensureInitialized(); | 250 ensureInitialized(); |
| 232 | 251 |
| 233 final testCase = new TestCase( | 252 final testCase = new TestCase( |
| 234 _tests.length + 1, _fullSpec(spec), body, callbacks); | 253 _tests.length + 1, _fullSpec(spec), body, callbacks); |
| 235 _tests.add(testCase); | 254 _tests.add(testCase); |
| 236 | 255 |
| 237 if (callbacks < 1) { | 256 if (callbacks < 1) { |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 327 return guardAsync( | 346 return guardAsync( |
| 328 () => _incrementCall() ? callback(arg1, arg2) : null, | 347 () => _incrementCall() ? callback(arg1, arg2) : null, |
| 329 () { if (calls == expectedCalls) callbackDone(); }); | 348 () { if (calls == expectedCalls) callbackDone(); }); |
| 330 } | 349 } |
| 331 | 350 |
| 332 /** Returns false if we exceded the number of expected calls. */ | 351 /** Returns false if we exceded the number of expected calls. */ |
| 333 bool _incrementCall() { | 352 bool _incrementCall() { |
| 334 calls++; | 353 calls++; |
| 335 if (calls > expectedCalls) { | 354 if (calls > expectedCalls) { |
| 336 testCase.error( | 355 testCase.error( |
| 337 'Callback called more times than expected ($expectedCalls)', | 356 'Callback called more times than expected ($calls > $expectedCalls)', |
| 338 ''); | 357 ''); |
| 339 _state = _UNCAUGHT_ERROR; | 358 _state = _UNCAUGHT_ERROR; |
| 340 return false; | 359 return false; |
| 341 } | 360 } |
| 342 return true; | 361 return true; |
| 343 } | 362 } |
| 344 } | 363 } |
| 345 | 364 |
| 346 /** | 365 /** |
| 347 * Indicate that [callback] is expected to be called a [count] number of times | 366 * Indicate that [callback] is expected to be called a [count] number of times |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 401 body(); | 420 body(); |
| 402 } finally { | 421 } finally { |
| 403 // Now that the group is over, restore the previous one. | 422 // Now that the group is over, restore the previous one. |
| 404 _currentGroup = oldGroup; | 423 _currentGroup = oldGroup; |
| 405 } | 424 } |
| 406 } | 425 } |
| 407 | 426 |
| 408 /** Called by subclasses to indicate that an asynchronous test completed. */ | 427 /** Called by subclasses to indicate that an asynchronous test completed. */ |
| 409 void callbackDone() { | 428 void callbackDone() { |
| 410 // TODO (gram): we defer this to give the nextBatch recursive | 429 // TODO (gram): we defer this to give the nextBatch recursive |
| 411 // stack a chance to unwind. This is a temporary hack but | 430 // stack a chance to unwind. This is a temporary hack but |
| 412 // really a bunch of code here needs to be fixed. We have a | 431 // really a bunch of code here needs to be fixed. We have a |
| 413 // single array that is being iterated through by nextBatch(), | 432 // single array that is being iterated through by nextBatch(), |
| 414 // which is recursively invoked in the case of async tests that | 433 // which is recursively invoked in the case of async tests that |
| 415 // run synchronously. Bad things can then happen. | 434 // run synchronously. Bad things can then happen. |
| 416 _defer(() { | 435 _defer(() { |
| 417 _callbacksCalled++; | 436 _callbacksCalled++; |
| 418 if (_currentTest < _tests.length) { | 437 if (_currentTest < _tests.length) { |
| 419 final testCase = _tests[_currentTest]; | 438 final testCase = _tests[_currentTest]; |
| 420 if (_callbacksCalled > testCase.callbacks) { | 439 if (_callbacksCalled > testCase.callbacks) { |
| 421 final expected = testCase.callbacks; | 440 final expected = testCase.callbacks; |
| 422 testCase.error( | 441 testCase.error( |
| 423 'More calls to callbackDone() than expected. ' | 442 'More calls to callbackDone() than expected. ' |
| 424 'Actual: ${_callbacksCalled}, expected: ${expected}', ''); | 443 'Actual: ${_callbacksCalled}, expected: ${expected}', ''); |
| 425 _state = _UNCAUGHT_ERROR; | 444 _state = _UNCAUGHT_ERROR; |
| 426 } else if ((_callbacksCalled == testCase.callbacks) && | 445 } else if ((_callbacksCalled == testCase.callbacks) && |
| 427 (_state != _RUNNING_TEST)) { | 446 (_state != _RUNNING_TEST)) { |
| 428 if (testCase.result == null) testCase.pass(); | 447 if (testCase.result == null) { |
| 448 testCase.pass(); |
| 449 } |
| 429 _currentTest++; | 450 _currentTest++; |
| 430 _testRunner(); | 451 _testRunner(); |
| 431 } | 452 } |
| 432 } | 453 } |
| 433 }); | 454 }); |
| 434 } | 455 } |
| 435 | 456 |
| 436 /** Menchanism to notify that an error was caught outside of this library. */ | 457 /** |
| 458 * Utility function that can be used to notify the test framework that an |
| 459 * error was caught outside of this library. |
| 460 */ |
| 437 void reportTestError(String msg, String trace) { | 461 void reportTestError(String msg, String trace) { |
| 438 if (_currentTest < _tests.length) { | 462 if (_currentTest < _tests.length) { |
| 439 final testCase = _tests[_currentTest]; | 463 final testCase = _tests[_currentTest]; |
| 440 testCase.error(msg, trace); | 464 testCase.error(msg, trace); |
| 441 _state = _UNCAUGHT_ERROR; | 465 _state = _UNCAUGHT_ERROR; |
| 442 if (testCase.callbacks > 0) { | 466 if (testCase.callbacks > 0) { |
| 443 _currentTest++; | 467 _currentTest++; |
| 444 _testRunner(); | 468 _testRunner(); |
| 445 } | 469 } |
| 446 } else { | 470 } else { |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 580 } | 604 } |
| 581 _config.onInit(); | 605 _config.onInit(); |
| 582 | 606 |
| 583 // Immediately queue the suite up. It will run after a timeout (i.e. after | 607 // Immediately queue the suite up. It will run after a timeout (i.e. after |
| 584 // main() has returned). | 608 // main() has returned). |
| 585 _defer(_runTests); | 609 _defer(_runTests); |
| 586 } | 610 } |
| 587 | 611 |
| 588 /** Signature for a test function. */ | 612 /** Signature for a test function. */ |
| 589 typedef void TestFunction(); | 613 typedef void TestFunction(); |
| OLD | NEW |