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 |