| 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] (see methods in |
| 16 * [Expectation]), [expectThrow], or using assertions with the [Expect] | 16 * [Expectation]), [expectThrow], or using assertions with the [Expect] |
| 17 * class. | 17 * class. |
| 18 * * Configuration: The framework can be adapted by calling [configure] with a | 18 * * Configuration: The framework can be adapted by calling [configure] with a |
| 19 * [Configuration]. Common configurations can be found in this package | 19 * [Configuration]. Common configurations can be found in this package |
| 20 * under: 'dom\_config.dart' (deprecated), 'html\_config.dart' (for running | 20 * under: 'dom\_config.dart', 'html\_config.dart', and 'vm\_config.dart'. |
| 21 * tests compiled to Javascript in a browser), and 'vm\_config.dart' (for | |
| 22 * running native Dart tests on the VM). | |
| 23 * | 21 * |
| 24 * ##Examples## | 22 * ##Examples## |
| 25 * | 23 * |
| 26 * A trivial test: | 24 * A trivial test: |
| 27 * | 25 * |
| 28 * #import('path-to-dart/lib/unittest/unitest.dart'); | 26 * #import('path-to-dart/lib/unittest/unitest.dart'); |
| 29 * main() { | 27 * main() { |
| 30 * test('this is a test', () { | 28 * test('this is a test', () { |
| 31 * int x = 2 + 3; | 29 * int x = 2 + 3; |
| 32 * expect(x).equals(5); | 30 * expect(x).equals(5); |
| (...skipping 29 matching lines...) Expand all Loading... |
| 62 * }); | 60 * }); |
| 63 * }); | 61 * }); |
| 64 * group('group B', () { | 62 * group('group B', () { |
| 65 * test('this B.1', () { | 63 * test('this B.1', () { |
| 66 * int x = 2 + 3; | 64 * int x = 2 + 3; |
| 67 * expect(x).equals(5); | 65 * expect(x).equals(5); |
| 68 * }); | 66 * }); |
| 69 * }); | 67 * }); |
| 70 * } | 68 * } |
| 71 * | 69 * |
| 72 * Asynchronous tests: if callbacks expect between 0 and 2 positional arguments, | 70 * Asynchronous tests: if callbacks expect between 0 and 2 positional arguments. |
| 73 * depending on the suffix of expectAsyncX(). expectAsyncX() will wrap a | |
| 74 * function into a new callback and will not consider the test complete until | |
| 75 * that callback is run. A count argument can be provided to specify the number | |
| 76 * of times the callback should be called (the default is 1). | |
| 77 * | 71 * |
| 78 * #import('path-to-dart/lib/unittest/unitest.dart'); | 72 * #import('path-to-dart/lib/unittest/unitest.dart'); |
| 79 * #import('dart:dom_deprecated'); | 73 * #import('dart:dom_deprecated'); |
| 80 * main() { | 74 * main() { |
| 81 * test('calllback is executed once', () { | 75 * test('calllback is executed once', () { |
| 82 * // wrap the callback of an asynchronous call with [expectAsync0] if | 76 * // wrap the callback of an asynchronous call with [expectAsync0] if |
| 83 * // the callback takes 0 arguments... | 77 * // the callback takes 0 arguments... |
| 84 * window.setTimeout(expectAsync0(() { | 78 * window.setTimeout(expectAsync0(() { |
| 85 * int x = 2 + 3; | 79 * int x = 2 + 3; |
| 86 * expect(x).equals(5); | 80 * expect(x).equals(5); |
| 87 * }), 0); | 81 * }), 0); |
| 88 * }); | 82 * }); |
| 89 * | 83 * |
| 90 * test('calllback is executed twice', () { | 84 * test('calllback is executed twice', () { |
| 91 * var callback = expectAsync0(() { | 85 * var callback = expectAsync0(() { |
| 92 * int x = 2 + 3; | 86 * int x = 2 + 3; |
| 93 * expect(x).equals(5); | 87 * expect(x).equals(5); |
| 94 * }, count: 2); // <-- we can indicate multiplicity to [expectAsync0] | 88 * }, count: 2); // <-- we can indicate multiplicity to [expectAsync0] |
| 95 * window.setTimeout(callback, 0); | 89 * window.setTimeout(callback, 0); |
| 96 * window.setTimeout(callback, 0); | 90 * window.setTimeout(callback, 0); |
| 97 * }); | 91 * }); |
| 98 * } | 92 * } |
| 99 * | 93 * |
| 100 * expectAsyncX() will wrap the callback code in a try/catch handler to handle | |
| 101 * exceptions (treated as test failures). There may be times when the number of | |
| 102 * times a callback should be called is non-deterministic. In this case a dummy | |
| 103 * callback can be created with expectAsync0((){}) and this can be called from | |
| 104 * the real callback when it is finally complete. In this case the body of the | |
| 105 * callback should be protected within a call to guardAsync(); this will ensure | |
| 106 * that exceptions are properly handled. | |
| 107 * | |
| 108 * Note: due to some language limitations we have to use different functions | 94 * Note: due to some language limitations we have to use different functions |
| 109 * depending on the number of positional arguments of the callback. In the | 95 * depending on the number of positional arguments of the callback. In the |
| 110 * future, we plan to expose a single `expectAsync` function that can be used | 96 * future, we plan to expose a single `expectAsync` function that can be used |
| 111 * regardless of the number of positional arguments. This requires new langauge | 97 * regardless of the number of positional arguments. This requires new langauge |
| 112 * features or fixes to the current spec (e.g. see | 98 * features or fixes to the current spec (e.g. see |
| 113 * [Issue 2706](http://dartbug.com/2706)). | 99 * [Issue 2706](http://dartbug.com/2706)). |
| 114 * | 100 * |
| 115 * Meanwhile, we plan to add this alternative API for callbacks of more than 2 | 101 * Meanwhile, we plan to add this alternative API for callbacks of more than 2 |
| 116 * arguments or that take named parameters. (this is not implemented yet, | 102 * arguments or that take named parameters. (this is not implemented yet, |
| 117 * but will be coming here soon). | 103 * but will be coming here soon). |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 229 * description will include the descriptions of any surrounding group() | 215 * description will include the descriptions of any surrounding group() |
| 230 * calls. | 216 * calls. |
| 231 */ | 217 */ |
| 232 void test(String spec, TestFunction body) { | 218 void test(String spec, TestFunction body) { |
| 233 ensureInitialized(); | 219 ensureInitialized(); |
| 234 | 220 |
| 235 _tests.add(new TestCase(_tests.length + 1, _fullSpec(spec), body, 0)); | 221 _tests.add(new TestCase(_tests.length + 1, _fullSpec(spec), body, 0)); |
| 236 } | 222 } |
| 237 | 223 |
| 238 /** | 224 /** |
| 225 * Creates a new async test case with the given description and body. The |
| 226 * description will include the descriptions of any surrounding group() |
| 227 * calls. |
| 228 */ |
| 229 // TODO(sigmund): deprecate this API |
| 230 void asyncTest(String spec, int callbacks, TestFunction body) { |
| 231 ensureInitialized(); |
| 232 |
| 233 final testCase = new TestCase( |
| 234 _tests.length + 1, _fullSpec(spec), body, callbacks); |
| 235 _tests.add(testCase); |
| 236 |
| 237 if (callbacks < 1) { |
| 238 testCase.error( |
| 239 'Async tests must wait for at least one callback ', ''); |
| 240 } |
| 241 } |
| 242 |
| 243 /** |
| 239 * Creates a new test case with the given description and body. The | 244 * Creates a new test case with the given description and body. The |
| 240 * description will include the descriptions of any surrounding group() | 245 * description will include the descriptions of any surrounding group() |
| 241 * calls. | 246 * calls. |
| 242 * | 247 * |
| 243 * "solo_" means that this will be the only test that is run. All other tests | 248 * "solo_" means that this will be the only test that is run. All other tests |
| 244 * will be skipped. This is a convenience function to let you quickly isolate | 249 * will be skipped. This is a convenience function to let you quickly isolate |
| 245 * a single test by adding "solo_" before it to temporarily disable all other | 250 * a single test by adding "solo_" before it to temporarily disable all other |
| 246 * tests. | 251 * tests. |
| 247 */ | 252 */ |
| 248 void solo_test(String spec, TestFunction body) { | 253 void solo_test(String spec, TestFunction body) { |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 296 } else if (arg3 == _sentinel) { | 301 } else if (arg3 == _sentinel) { |
| 297 return callback(arg0, arg1, arg2); | 302 return callback(arg0, arg1, arg2); |
| 298 } else if (arg4 == _sentinel) { | 303 } else if (arg4 == _sentinel) { |
| 299 return callback(arg0, arg1, arg2, arg3); | 304 return callback(arg0, arg1, arg2, arg3); |
| 300 } else { | 305 } else { |
| 301 testCase.error( | 306 testCase.error( |
| 302 'unittest lib does not support callbacks with more than 4 arguments', | 307 'unittest lib does not support callbacks with more than 4 arguments', |
| 303 ''); | 308 ''); |
| 304 _state = _UNCAUGHT_ERROR; | 309 _state = _UNCAUGHT_ERROR; |
| 305 } | 310 } |
| 306 }, () { if (calls == expectedCalls) _callbackDone(); }); | 311 }, () { if (calls == expectedCalls) callbackDone(); }); |
| 307 } | 312 } |
| 308 | 313 |
| 309 invoke0() { | 314 invoke0() { |
| 310 return guardAsync( | 315 return guardAsync( |
| 311 () => _incrementCall() ? callback() : null, | 316 () => _incrementCall() ? callback() : null, |
| 312 () { if (calls == expectedCalls) _callbackDone(); }); | 317 () { if (calls == expectedCalls) callbackDone(); }); |
| 313 } | 318 } |
| 314 | 319 |
| 315 invoke1(arg1) { | 320 invoke1(arg1) { |
| 316 return guardAsync( | 321 return guardAsync( |
| 317 () => _incrementCall() ? callback(arg1) : null, | 322 () => _incrementCall() ? callback(arg1) : null, |
| 318 () { if (calls == expectedCalls) _callbackDone(); }); | 323 () { if (calls == expectedCalls) callbackDone(); }); |
| 319 } | 324 } |
| 320 | 325 |
| 321 invoke2(arg1, arg2) { | 326 invoke2(arg1, arg2) { |
| 322 return guardAsync( | 327 return guardAsync( |
| 323 () => _incrementCall() ? callback(arg1, arg2) : null, | 328 () => _incrementCall() ? callback(arg1, arg2) : null, |
| 324 () { if (calls == expectedCalls) _callbackDone(); }); | 329 () { if (calls == expectedCalls) callbackDone(); }); |
| 325 } | 330 } |
| 326 | 331 |
| 327 /** Returns false if we exceded the number of expected calls. */ | 332 /** Returns false if we exceded the number of expected calls. */ |
| 328 bool _incrementCall() { | 333 bool _incrementCall() { |
| 329 calls++; | 334 calls++; |
| 330 if (calls > expectedCalls) { | 335 if (calls > expectedCalls) { |
| 331 testCase.error( | 336 testCase.error( |
| 332 'Callback called more times than expected ($expectedCalls)', | 337 'Callback called more times than expected ($expectedCalls)', |
| 333 ''); | 338 ''); |
| 334 _state = _UNCAUGHT_ERROR; | 339 _state = _UNCAUGHT_ERROR; |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 394 | 399 |
| 395 try { | 400 try { |
| 396 body(); | 401 body(); |
| 397 } finally { | 402 } finally { |
| 398 // Now that the group is over, restore the previous one. | 403 // Now that the group is over, restore the previous one. |
| 399 _currentGroup = oldGroup; | 404 _currentGroup = oldGroup; |
| 400 } | 405 } |
| 401 } | 406 } |
| 402 | 407 |
| 403 /** Called by subclasses to indicate that an asynchronous test completed. */ | 408 /** Called by subclasses to indicate that an asynchronous test completed. */ |
| 404 void _callbackDone() { | 409 void callbackDone() { |
| 405 // TODO (gram): we defer this to give the nextBatch recursive | 410 // TODO (gram): we defer this to give the nextBatch recursive |
| 406 // stack a chance to unwind. This is a temporary hack but | 411 // stack a chance to unwind. This is a temporary hack but |
| 407 // really a bunch of code here needs to be fixed. We have a | 412 // really a bunch of code here needs to be fixed. We have a |
| 408 // single array that is being iterated through by nextBatch(), | 413 // single array that is being iterated through by nextBatch(), |
| 409 // which is recursively invoked in the case of async tests that | 414 // which is recursively invoked in the case of async tests that |
| 410 // run synchronously. Bad things can then happen. | 415 // run synchronously. Bad things can then happen. |
| 411 _defer(() { | 416 _defer(() { |
| 412 _callbacksCalled++; | 417 _callbacksCalled++; |
| 413 if (_currentTest < _tests.length) { | 418 if (_currentTest < _tests.length) { |
| 414 final testCase = _tests[_currentTest]; | 419 final testCase = _tests[_currentTest]; |
| 415 if (_callbacksCalled > testCase.callbacks) { | 420 if (_callbacksCalled > testCase.callbacks) { |
| 416 final expected = testCase.callbacks; | 421 final expected = testCase.callbacks; |
| 417 testCase.error( | 422 testCase.error( |
| 418 'More calls to _callbackDone() than expected. ' | 423 'More calls to callbackDone() than expected. ' |
| 419 'Actual: ${_callbacksCalled}, expected: ${expected}', ''); | 424 'Actual: ${_callbacksCalled}, expected: ${expected}', ''); |
| 420 _state = _UNCAUGHT_ERROR; | 425 _state = _UNCAUGHT_ERROR; |
| 421 } else if ((_callbacksCalled == testCase.callbacks) && | 426 } else if ((_callbacksCalled == testCase.callbacks) && |
| 422 (_state != _RUNNING_TEST)) { | 427 (_state != _RUNNING_TEST)) { |
| 423 if (testCase.result == null) testCase.pass(); | 428 if (testCase.result == null) testCase.pass(); |
| 424 _currentTest++; | 429 _currentTest++; |
| 425 _testRunner(); | 430 _testRunner(); |
| 426 } | 431 } |
| 427 } | 432 } |
| 428 }); | 433 }); |
| 429 } | 434 } |
| 430 | 435 |
| 431 /** | 436 /** Menchanism to notify that an error was caught outside of this library. */ |
| 432 * Utility function that can be used to notify the test framework that an | |
| 433 * error was caught outside of this library. | |
| 434 */ | |
| 435 void reportTestError(String msg, String trace) { | 437 void reportTestError(String msg, String trace) { |
| 436 if (_currentTest < _tests.length) { | 438 if (_currentTest < _tests.length) { |
| 437 final testCase = _tests[_currentTest]; | 439 final testCase = _tests[_currentTest]; |
| 438 testCase.error(msg, trace); | 440 testCase.error(msg, trace); |
| 439 _state = _UNCAUGHT_ERROR; | 441 _state = _UNCAUGHT_ERROR; |
| 440 if (testCase.callbacks > 0) { | 442 if (testCase.callbacks > 0) { |
| 441 _currentTest++; | 443 _currentTest++; |
| 442 _testRunner(); | 444 _testRunner(); |
| 443 } | 445 } |
| 444 } else { | 446 } else { |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 578 } | 580 } |
| 579 _config.onInit(); | 581 _config.onInit(); |
| 580 | 582 |
| 581 // Immediately queue the suite up. It will run after a timeout (i.e. after | 583 // Immediately queue the suite up. It will run after a timeout (i.e. after |
| 582 // main() has returned). | 584 // main() has returned). |
| 583 _defer(_runTests); | 585 _defer(_runTests); |
| 584 } | 586 } |
| 585 | 587 |
| 586 /** Signature for a test function. */ | 588 /** Signature for a test function. */ |
| 587 typedef void TestFunction(); | 589 typedef void TestFunction(); |
| OLD | NEW |