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 |