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 |