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