OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
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. | |
4 | |
5 /// Support for writing Dart unit tests. | |
6 /// | |
7 /// For information on installing and importing this library, see the | |
8 /// [unittest package on pub.dartlang.org] | |
9 /// (http://pub.dartlang.org/packages/unittest). | |
10 /// | |
11 /// **See also:** | |
12 /// [Unit Testing with Dart] | |
13 /// (http://www.dartlang.org/articles/dart-unit-tests/) | |
14 /// | |
15 /// ##Concepts | |
16 /// | |
17 /// * __Tests__: Tests are specified via the top-level function [test], they ca
n be | |
18 /// organized together using [group]. | |
19 /// | |
20 /// * __Checks__: Test expectations can be specified via [expect] | |
21 /// | |
22 /// * __Matchers__: [expect] assertions are written declaratively using the | |
23 /// [Matcher] class. | |
24 /// | |
25 /// * __Configuration__: The framework can be adapted by setting | |
26 /// [unittestConfiguration] with a [Configuration]. See the other libraries | |
27 /// in the `unittest` package for alternative implementations of | |
28 /// [Configuration] including `compact_vm_config.dart`, `html_config.dart` | |
29 /// and `html_enhanced_config.dart`. | |
30 /// | |
31 /// ##Examples | |
32 /// | |
33 /// A trivial test: | |
34 /// | |
35 /// import 'package:unittest/unittest.dart'; | |
36 /// main() { | |
37 /// test('this is a test', () { | |
38 /// int x = 2 + 3; | |
39 /// expect(x, equals(5)); | |
40 /// }); | |
41 /// } | |
42 /// | |
43 /// Multiple tests: | |
44 /// | |
45 /// import 'package:unittest/unittest.dart'; | |
46 /// main() { | |
47 /// test('this is a test', () { | |
48 /// int x = 2 + 3; | |
49 /// expect(x, equals(5)); | |
50 /// }); | |
51 /// test('this is another test', () { | |
52 /// int x = 2 + 3; | |
53 /// expect(x, equals(5)); | |
54 /// }); | |
55 /// } | |
56 /// | |
57 /// Multiple tests, grouped by category: | |
58 /// | |
59 /// import 'package:unittest/unittest.dart'; | |
60 /// main() { | |
61 /// group('group A', () { | |
62 /// test('test A.1', () { | |
63 /// int x = 2 + 3; | |
64 /// expect(x, equals(5)); | |
65 /// }); | |
66 /// test('test A.2', () { | |
67 /// int x = 2 + 3; | |
68 /// expect(x, equals(5)); | |
69 /// }); | |
70 /// }); | |
71 /// group('group B', () { | |
72 /// test('this B.1', () { | |
73 /// int x = 2 + 3; | |
74 /// expect(x, equals(5)); | |
75 /// }); | |
76 /// }); | |
77 /// } | |
78 /// | |
79 /// Asynchronous tests: if callbacks expect between 0 and 6 positional | |
80 /// arguments, [expectAsync] will wrap a function into a new callback and will | |
81 /// not consider the test complete until that callback is run. A count argument | |
82 /// can be provided to specify the number of times the callback should be called | |
83 /// (the default is 1). | |
84 /// | |
85 /// import 'dart:async'; | |
86 /// import 'package:unittest/unittest.dart'; | |
87 /// void main() { | |
88 /// test('callback is executed once', () { | |
89 /// // wrap the callback of an asynchronous call with [expectAsync] if | |
90 /// // the callback takes 0 arguments... | |
91 /// var timer = Timer.run(expectAsync(() { | |
92 /// int x = 2 + 3; | |
93 /// expect(x, equals(5)); | |
94 /// })); | |
95 /// }); | |
96 /// | |
97 /// test('callback is executed twice', () { | |
98 /// var callback = expectAsync(() { | |
99 /// int x = 2 + 3; | |
100 /// expect(x, equals(5)); | |
101 /// }, count: 2); // <-- we can indicate multiplicity to [expectAsync] | |
102 /// Timer.run(callback); | |
103 /// Timer.run(callback); | |
104 /// }); | |
105 /// } | |
106 /// | |
107 /// There may be times when the number of times a callback should be called is | |
108 /// non-deterministic. In this case a dummy callback can be created with | |
109 /// expectAsync((){}) and this can be called from the real callback when it is | |
110 /// finally complete. | |
111 /// | |
112 /// A variation on this is [expectAsyncUntil], which takes a callback as the | |
113 /// first parameter and a predicate function as the second parameter. After each | |
114 /// time the callback is called, the predicate function will be called. If it | |
115 /// returns `false` the test will still be considered incomplete. | |
116 /// | |
117 /// Test functions can return [Future]s, which provide another way of doing | |
118 /// asynchronous tests. The test framework will handle exceptions thrown by | |
119 /// the Future, and will advance to the next test when the Future is complete. | |
120 /// | |
121 /// import 'dart:async'; | |
122 /// import 'package:unittest/unittest.dart'; | |
123 /// void main() { | |
124 /// test('test that time has passed', () { | |
125 /// var duration = const Duration(milliseconds: 200); | |
126 /// var time = new DateTime.now(); | |
127 /// | |
128 /// return new Future.delayed(duration).then((_) { | |
129 /// var delta = new DateTime.now().difference(time); | |
130 /// | |
131 /// expect(delta, greaterThanOrEqualTo(duration)); | |
132 /// }); | |
133 /// }); | |
134 /// } | |
135 library unittest; | |
136 | |
137 import 'dart:async'; | |
138 import 'dart:collection'; | |
139 import 'dart:isolate'; | |
140 | |
141 import 'package:matcher/matcher.dart' show DefaultFailureHandler, | |
142 configureExpectFailureHandler, TestFailure, wrapAsync; | |
143 export 'package:matcher/matcher.dart'; | |
144 | |
145 import 'src/utils.dart'; | |
146 | |
147 import 'src/configuration.dart'; | |
148 export 'src/configuration.dart'; | |
149 | |
150 part 'src/simple_configuration.dart'; | |
151 part 'src/group_context.dart'; | |
152 part 'src/spread_args_helper.dart'; | |
153 part 'src/test_case.dart'; | |
154 part 'src/test_environment.dart'; | |
155 | |
156 const Symbol _UNITTEST_ENVIRONMENT = #unittest.environment; | |
157 | |
158 final _TestEnvironment _defaultEnvironment = new _TestEnvironment(); | |
159 | |
160 /** | |
161 * Internal getter for the current unittest config. | |
162 */ | |
163 _TestEnvironment get _environment { | |
164 var environment = Zone.current[_UNITTEST_ENVIRONMENT]; | |
165 if (environment == null) return _defaultEnvironment; | |
166 return environment; | |
167 } | |
168 | |
169 // Convenience getter for the current environment's config. | |
170 Configuration get _config => _environment.config; | |
171 | |
172 // Convenience setter for the current environment's config. | |
173 void set _config(Configuration config) { | |
174 _environment.config = config; | |
175 } | |
176 | |
177 // Convenience getter for the current environment's test cases. | |
178 List<TestCase> get _testCases => _environment.testCases; | |
179 | |
180 /// [Configuration] used by the unittest library. | |
181 /// | |
182 /// Note that if a configuration has not been set, calling this getter will | |
183 /// create a default configuration. | |
184 Configuration get unittestConfiguration { | |
185 if (_config == null) { | |
186 _config = new Configuration(); | |
187 } | |
188 return _config; | |
189 } | |
190 | |
191 /// Sets the [Configuration] used by the unittest library. | |
192 /// | |
193 /// Throws a [StateError] if there is an existing, incompatible value. | |
194 void set unittestConfiguration(Configuration value) { | |
195 if (!identical(_config, value)) { | |
196 if (_config != null) { | |
197 logMessage('Warning: The unittestConfiguration has already been set. New ' | |
198 'unittestConfiguration ignored.'); | |
199 } else { | |
200 _config = value; | |
201 } | |
202 } | |
203 } | |
204 | |
205 /// Can be called by tests to log status. Tests should use this | |
206 /// instead of [print]. | |
207 void logMessage(String message) => | |
208 _config.onLogMessage(currentTestCase, message); | |
209 | |
210 /// Separator used between group names and test names. | |
211 String groupSep = ' '; | |
212 | |
213 /// Tests executed in this suite. | |
214 List<TestCase> get testCases => | |
215 new UnmodifiableListView<TestCase>(_environment.testCases); | |
216 | |
217 /// Interval (in msecs) after which synchronous tests will insert an async | |
218 /// delay to allow DOM or other updates. | |
219 const int BREATH_INTERVAL = 200; | |
220 | |
221 /// [TestCase] currently being executed. | |
222 TestCase get currentTestCase => | |
223 (_environment.currentTestCaseIndex >= 0 && | |
224 _environment.currentTestCaseIndex < testCases.length) | |
225 ? testCases[_environment.currentTestCaseIndex] | |
226 : null; | |
227 | |
228 /* Test case result strings. */ | |
229 // TODO(gram) we should change these constants to use a different string | |
230 // (so that writing 'FAIL' in the middle of a test doesn't | |
231 // imply that the test fails). We can't do it without also changing | |
232 // the testrunner and test.dart though. | |
233 /// Result string for a passing test case. | |
234 const PASS = 'pass'; | |
235 /// Result string for a failing test case. | |
236 const FAIL = 'fail'; | |
237 /// Result string for an test case with an error. | |
238 const ERROR = 'error'; | |
239 | |
240 /// Creates a new test case with the given description and body. The | |
241 /// description will include the descriptions of any surrounding group() | |
242 /// calls. | |
243 void test(String spec, TestFunction body) { | |
244 _requireNotRunning(); | |
245 ensureInitialized(); | |
246 if (!_environment.soloTestSeen || _environment.soloNestingLevel > 0) { | |
247 var testcase = new TestCase._internal(testCases.length + 1, _fullSpec(spec), | |
248 body); | |
249 _testCases.add(testcase); | |
250 } | |
251 } | |
252 | |
253 /// Convenience function for skipping a test. | |
254 void skip_test(String spec, TestFunction body) {} | |
255 | |
256 /// Creates a new test case with the given description and body. The | |
257 /// description will include the descriptions of any surrounding group() | |
258 /// calls. | |
259 /// | |
260 /// If we use [solo_test] (or [solo_group]) instead of test, then all non-solo | |
261 /// tests will be disabled. Note that if we use [solo_group], all tests in | |
262 /// the group will be enabled, regardless of whether they use [test] or | |
263 /// [solo_test], or whether they are in a nested [group] vs [solo_group]. Put | |
264 /// another way, if there are any calls to [solo_test] or [solo_group] in a test | |
265 /// file, all tests that are not inside a [solo_group] will be disabled unless | |
266 /// they are [solo_test]s. | |
267 /// | |
268 /// [skip_test] and [skip_group] take precedence over soloing, by virtue of the | |
269 /// fact that they are effectively no-ops. | |
270 void solo_test(String spec, TestFunction body) { | |
271 _requireNotRunning(); | |
272 ensureInitialized(); | |
273 if (!_environment.soloTestSeen) { | |
274 _environment.soloTestSeen = true; | |
275 // This is the first solo-ed test. Discard all tests up to now. | |
276 _testCases.clear(); | |
277 } | |
278 ++_environment.soloNestingLevel; | |
279 try { | |
280 test(spec, body); | |
281 } finally { | |
282 --_environment.soloNestingLevel; | |
283 } | |
284 } | |
285 | |
286 /// Indicate that [callback] is expected to be called a [count] number of times | |
287 /// (by default 1). | |
288 /// | |
289 /// The unittest framework will wait for the callback to run the | |
290 /// specified [count] times before it continues with the following test. Using | |
291 /// [expectAsync] will also ensure that errors that occur within [callback] are | |
292 /// tracked and reported. [callback] should take 0 positional arguments (named | |
293 /// arguments are not supported). [id] can be used to provide more | |
294 /// descriptive error messages if the callback is called more often than | |
295 /// expected. | |
296 /// | |
297 /// [max] can be used to specify an upper bound on the number of | |
298 /// calls; if this is exceeded the test will fail (or be marked as in error if | |
299 /// it was already complete). A value of 0 for [max] (the default) will set | |
300 /// the upper bound to the same value as [count]; i.e. the callback should be | |
301 /// called exactly [count] times. A value of -1 for [max] will mean no upper | |
302 /// bound. | |
303 /// | |
304 /// [reason] is optional and is typically not supplied, as a reason is generated | |
305 /// by the unittest package; if reason is included it is appended to the | |
306 /// generated reason. | |
307 Function expectAsync(Function callback, | |
308 {int count: 1, int max: 0, String id, String reason}) => | |
309 new _SpreadArgsHelper(callback, count, max, id, reason).func; | |
310 | |
311 /// Indicate that [callback] is expected to be called until [isDone] returns | |
312 /// true. | |
313 /// | |
314 /// The unittest framework checks [isDone] after each callback and only | |
315 /// when it returns true will it continue with the following test. Using | |
316 /// [expectAsyncUntil] will also ensure that errors that occur within | |
317 /// [callback] are tracked and reported. [callback] should take 0 positional | |
318 /// arguments (named arguments are not supported). [id] can be used to | |
319 /// identify the callback in error messages (for example if it is called | |
320 /// after the test case is complete). | |
321 /// | |
322 /// [reason] is optional and is typically not supplied, as a reason is generated | |
323 /// by the unittest package; if reason is included it is appended to the | |
324 /// generated reason. | |
325 Function expectAsyncUntil(Function callback, bool isDone(), | |
326 {String id, String reason}) => | |
327 new _SpreadArgsHelper(callback, 0, -1, id, reason, isDone: isDone).func; | |
328 | |
329 /// Creates a new named group of tests. | |
330 /// | |
331 /// Calls to group() or test() within the body of the function passed to this | |
332 /// named group will inherit this group's description. | |
333 void group(String description, void body()) { | |
334 ensureInitialized(); | |
335 _requireNotRunning(); | |
336 _environment.currentContext = | |
337 new _GroupContext(_environment.currentContext, description); | |
338 try { | |
339 body(); | |
340 } catch (e, trace) { | |
341 var stack = (trace == null) ? '' : ': ${trace.toString()}'; | |
342 _environment.uncaughtErrorMessage = "${e.toString()}$stack"; | |
343 } finally { | |
344 // Now that the group is over, restore the previous one. | |
345 _environment.currentContext = _environment.currentContext.parent; | |
346 } | |
347 } | |
348 | |
349 /// Like [skip_test], but for groups. | |
350 void skip_group(String description, void body()) {} | |
351 | |
352 /// Like [solo_test], but for groups. | |
353 void solo_group(String description, void body()) { | |
354 _requireNotRunning(); | |
355 ensureInitialized(); | |
356 if (!_environment.soloTestSeen) { | |
357 _environment.soloTestSeen = true; | |
358 // This is the first solo-ed group. Discard all tests up to now. | |
359 _testCases.clear(); | |
360 } | |
361 ++_environment.soloNestingLevel; | |
362 try { | |
363 group(description, body); | |
364 } finally { | |
365 --_environment.soloNestingLevel; | |
366 } | |
367 } | |
368 | |
369 /// Register a [setUp] function for a test [group]. | |
370 /// | |
371 /// This function will be called before each test in the group is run. | |
372 /// [setUp] and [tearDown] should be called within the [group] before any | |
373 /// calls to [test]. The [setupTest] function can be asynchronous; in this | |
374 /// case it must return a [Future]. | |
375 void setUp(Function setupTest) { | |
376 _requireNotRunning(); | |
377 _environment.currentContext.testSetup = setupTest; | |
378 } | |
379 | |
380 /// Register a [tearDown] function for a test [group]. | |
381 /// | |
382 /// This function will be called after each test in the group is run. | |
383 /// | |
384 /// Note that if groups are nested only the most locally scoped [teardownTest] | |
385 /// function will be run. [setUp] and [tearDown] should be called within the | |
386 /// [group] before any calls to [test]. The [teardownTest] function can be | |
387 /// asynchronous; in this case it must return a [Future]. | |
388 void tearDown(Function teardownTest) { | |
389 _requireNotRunning(); | |
390 _environment.currentContext.testTeardown = teardownTest; | |
391 } | |
392 | |
393 /// Advance to the next test case. | |
394 void _nextTestCase() { | |
395 _environment.currentTestCaseIndex++; | |
396 _runTest(); | |
397 } | |
398 | |
399 /// Handle errors that happen outside the tests. | |
400 // TODO(vsm): figure out how to expose the stack trace here | |
401 // Currently e.message works in dartium, but not in dartc. | |
402 void handleExternalError(e, String message, [stack]) { | |
403 var msg = '$message\nCaught $e'; | |
404 | |
405 if (currentTestCase != null) { | |
406 currentTestCase._error(msg, stack); | |
407 } else { | |
408 _environment.uncaughtErrorMessage = "$msg: $stack"; | |
409 } | |
410 } | |
411 | |
412 /// Filter the tests by [testFilter]. | |
413 /// | |
414 /// [testFilter] can be a [RegExp], a [String] or a | |
415 /// predicate function. This is different from enabling or disabling tests | |
416 /// in that it removes the tests completely. | |
417 void filterTests(testFilter) { | |
418 var filterFunction; | |
419 if (testFilter is String) { | |
420 RegExp re = new RegExp(testFilter); | |
421 filterFunction = (t) => re.hasMatch(t.description); | |
422 } else if (testFilter is RegExp) { | |
423 filterFunction = (t) => testFilter.hasMatch(t.description); | |
424 } else if (testFilter is Function) { | |
425 filterFunction = testFilter; | |
426 } | |
427 _testCases.retainWhere(filterFunction); | |
428 } | |
429 | |
430 /// Runs all queued tests, one at a time. | |
431 void runTests() { | |
432 _requireNotRunning(); | |
433 _ensureInitialized(false); | |
434 _environment.currentTestCaseIndex = 0; | |
435 _config.onStart(); | |
436 _runTest(); | |
437 } | |
438 | |
439 /// Registers that an exception was caught for the current test. | |
440 void registerException(e, [trace]) { | |
441 _registerException(currentTestCase, e, trace); | |
442 } | |
443 | |
444 /// Registers that an exception was caught for the current test. | |
445 void _registerException(TestCase testCase, e, [trace]) { | |
446 String message = (e is TestFailure) ? e.message : 'Caught $e'; | |
447 if (testCase.result == null) { | |
448 testCase._fail(message, trace); | |
449 } else { | |
450 testCase._error(message, trace); | |
451 } | |
452 } | |
453 | |
454 /// Runs the next test. | |
455 void _runTest() { | |
456 if (_environment.currentTestCaseIndex >= testCases.length) { | |
457 assert(_environment.currentTestCaseIndex == testCases.length); | |
458 _completeTests(); | |
459 } else { | |
460 var testCase = testCases[_environment.currentTestCaseIndex]; | |
461 Future f = runZoned(testCase._run, onError: (error, stack) { | |
462 // TODO(kevmoo) Do a better job of flagging these are async errors. | |
463 // https://code.google.com/p/dart/issues/detail?id=16530 | |
464 _registerException(testCase, error, stack); | |
465 }); | |
466 | |
467 var timeout = unittestConfiguration.timeout; | |
468 | |
469 Timer timer; | |
470 if (timeout != null) { | |
471 try { | |
472 timer = new Timer(timeout, () { | |
473 testCase._error("Test timed out after ${timeout.inSeconds} seconds."); | |
474 _nextTestCase(); | |
475 }); | |
476 } on UnsupportedError catch (e) { | |
477 if (e.message != "Timer greater than 0.") rethrow; | |
478 // Support running on d8 and jsshell which don't support timers. | |
479 } | |
480 } | |
481 f.whenComplete(() { | |
482 if (timer != null) timer.cancel(); | |
483 var now = new DateTime.now().millisecondsSinceEpoch; | |
484 if ((now - _environment.lastBreath) >= BREATH_INTERVAL) { | |
485 _environment.lastBreath = now; | |
486 Timer.run(_nextTestCase); | |
487 } else { | |
488 scheduleMicrotask(_nextTestCase); // Schedule the next test. | |
489 } | |
490 }); | |
491 } | |
492 } | |
493 | |
494 /// Publish results on the page and notify controller. | |
495 void _completeTests() { | |
496 if (!_environment.initialized) return; | |
497 int passed = 0; | |
498 int failed = 0; | |
499 int errors = 0; | |
500 | |
501 for (TestCase t in testCases) { | |
502 switch (t.result) { | |
503 case PASS: passed++; break; | |
504 case FAIL: failed++; break; | |
505 case ERROR: errors++; break; | |
506 } | |
507 } | |
508 _config.onSummary(passed, failed, errors, testCases, | |
509 _environment.uncaughtErrorMessage); | |
510 _config.onDone(passed > 0 && failed == 0 && errors == 0 && | |
511 _environment.uncaughtErrorMessage == null); | |
512 _environment.initialized = false; | |
513 _environment.currentTestCaseIndex = -1; | |
514 } | |
515 | |
516 String _fullSpec(String spec) { | |
517 var group = '${_environment.currentContext.fullName}'; | |
518 if (spec == null) return group; | |
519 return group != '' ? '$group$groupSep$spec' : spec; | |
520 } | |
521 | |
522 /// Lazily initializes the test library if not already initialized. | |
523 void ensureInitialized() { | |
524 _ensureInitialized(true); | |
525 } | |
526 | |
527 void _ensureInitialized(bool configAutoStart) { | |
528 if (_environment.initialized) { | |
529 return; | |
530 } | |
531 _environment.initialized = true; | |
532 // Hook our async guard into the matcher library. | |
533 wrapAsync = (f, [id]) => expectAsync(f, id: id); | |
534 | |
535 _environment.uncaughtErrorMessage = null; | |
536 | |
537 unittestConfiguration.onInit(); | |
538 | |
539 if (configAutoStart && _config.autoStart) { | |
540 // Immediately queue the suite up. It will run after a timeout (i.e. after | |
541 // main() has returned). | |
542 scheduleMicrotask(runTests); | |
543 } | |
544 } | |
545 | |
546 /// Select a solo test by ID. | |
547 void setSoloTest(int id) => _testCases.retainWhere((t) => t.id == id); | |
548 | |
549 /// Enable/disable a test by ID. | |
550 void _setTestEnabledState(int testId, bool state) { | |
551 // Try fast path first. | |
552 if (testCases.length > testId && testCases[testId].id == testId) { | |
553 testCases[testId]._enabled = state; | |
554 } else { | |
555 for (var i = 0; i < testCases.length; i++) { | |
556 if (testCases[i].id == testId) { | |
557 testCases[i]._enabled = state; | |
558 break; | |
559 } | |
560 } | |
561 } | |
562 } | |
563 | |
564 /// Enable a test by ID. | |
565 void enableTest(int testId) => _setTestEnabledState(testId, true); | |
566 | |
567 /// Disable a test by ID. | |
568 void disableTest(int testId) => _setTestEnabledState(testId, false); | |
569 | |
570 /// Signature for a test function. | |
571 typedef dynamic TestFunction(); | |
572 | |
573 /// A flag that controls whether we hide unittest and core library details in | |
574 /// exception stacks. | |
575 /// | |
576 /// Useful to disable when debugging unittest or matcher customizations. | |
577 bool formatStacks = true; | |
578 | |
579 /// A flag that controls whether we try to filter out irrelevant frames from | |
580 /// the stack trace. | |
581 /// | |
582 /// Requires [formatStacks] to be set. | |
583 bool filterStacks = true; | |
584 | |
585 void _requireNotRunning() { | |
586 if (_environment.currentTestCaseIndex != -1) { | |
587 throw new StateError('Not allowed when tests are running.'); | |
588 } | |
589 } | |
590 | |
591 /// Method to create a test environment running in its own zone scope. | |
592 /// | |
593 /// This allows for multiple invocations of the unittest library in the same | |
594 /// application instance. | |
595 /// This is useful when, for example, creating a test runner application which | |
596 /// needs to create a new pristine test environment on each invocation to run | |
597 /// a given set of test. | |
598 dynamic withTestEnvironment(callback()) { | |
599 return runZoned(callback, | |
600 zoneValues: {_UNITTEST_ENVIRONMENT: new _TestEnvironment()}); | |
601 } | |
OLD | NEW |