| OLD | NEW |
| (Empty) |
| 1 A package for writing readable tests of asynchronous behavior. | |
| 2 | |
| 3 This package works by building up a queue of asynchronous tasks called a | |
| 4 "schedule", then executing those tasks in order. This allows the tests to | |
| 5 read like synchronous, linear code, despite executing asynchronously. | |
| 6 | |
| 7 The `scheduled_test` package is built on top of `unittest`, and should be | |
| 8 imported instead of `unittest`. It provides its own version of [group], | |
| 9 [test], and [setUp], and re-exports most other APIs from unittest. | |
| 10 | |
| 11 To schedule a task, call the [schedule] function. For example: | |
| 12 | |
| 13 ```dart | |
| 14 import 'package:scheduled_test/scheduled_test.dart'; | |
| 15 | |
| 16 void main() { | |
| 17 test('writing to a file and reading it back should work', () { | |
| 18 schedule(() { | |
| 19 // The schedule won't proceed until the returned Future has | |
| 20 // completed. | |
| 21 return new File("output.txt").writeAsString("contents"); | |
| 22 }); | |
| 23 | |
| 24 schedule(() { | |
| 25 return new File("output.txt").readAsString().then((contents) { | |
| 26 // The normal unittest matchers can still be used. | |
| 27 expect(contents, equals("contents")); | |
| 28 }); | |
| 29 }); | |
| 30 }); | |
| 31 } | |
| 32 ``` | |
| 33 | |
| 34 ## Setting up and tearing down | |
| 35 | |
| 36 The `scheduled_test` package defines its own [setUp] method that works just | |
| 37 like the one in `unittest`. Tasks can be scheduled in [setUp]; they'll be | |
| 38 run before the tasks scheduled by tests in that group. [currentSchedule] is | |
| 39 also set in the [setUp] callback. | |
| 40 | |
| 41 This package doesn't have an explicit `tearDown` method. Instead, the | |
| 42 [currentSchedule.onComplete] and [currentSchedule.onException] task queues | |
| 43 can have tasks scheduled during [setUp]. For example: | |
| 44 | |
| 45 ```dart | |
| 46 import 'package:scheduled_test/scheduled_test.dart'; | |
| 47 | |
| 48 void main() { | |
| 49 var tempDir; | |
| 50 setUp(() { | |
| 51 schedule(() { | |
| 52 return createTempDir().then((dir) { | |
| 53 tempDir = dir; | |
| 54 }); | |
| 55 }); | |
| 56 | |
| 57 currentSchedule.onComplete.schedule(() => deleteDir(tempDir)); | |
| 58 }); | |
| 59 | |
| 60 // ... | |
| 61 } | |
| 62 ``` | |
| 63 | |
| 64 ## Passing values between tasks | |
| 65 | |
| 66 It's often useful to use values computed in one task in other tasks that are | |
| 67 scheduled afterwards. There are two ways to do this. The most | |
| 68 straightforward is just to define a local variable and assign to it. For | |
| 69 example: | |
| 70 | |
| 71 ```dart | |
| 72 import 'package:scheduled_test/scheduled_test.dart'; | |
| 73 | |
| 74 void main() { | |
| 75 test('computeValue returns 12', () { | |
| 76 var value; | |
| 77 | |
| 78 schedule(() { | |
| 79 return computeValue().then((computedValue) { | |
| 80 value = computedValue; | |
| 81 }); | |
| 82 }); | |
| 83 | |
| 84 schedule(() => expect(value, equals(12))); | |
| 85 }); | |
| 86 } | |
| 87 ``` | |
| 88 | |
| 89 However, this doesn't scale well, especially when you start factoring out | |
| 90 calls to [schedule] into library methods. For that reason, [schedule] | |
| 91 returns a [Future] that will complete to the same value as the return | |
| 92 value of the task. For example: | |
| 93 | |
| 94 ```dart | |
| 95 import 'package:scheduled_test/scheduled_test.dart'; | |
| 96 | |
| 97 void main() { | |
| 98 test('computeValue returns 12', () { | |
| 99 var valueFuture = schedule(() => computeValue()); | |
| 100 schedule(() { | |
| 101 valueFuture.then((value) => expect(value, equals(12))); | |
| 102 }); | |
| 103 }); | |
| 104 } | |
| 105 ``` | |
| 106 | |
| 107 ## Out-of-Band Callbacks | |
| 108 | |
| 109 Sometimes your tests will have callbacks that don't fit into the schedule. | |
| 110 It's important that errors in these callbacks are still registered, though, | |
| 111 and that [Schedule.onException] and [Schedule.onComplete] still run after | |
| 112 they finish. When using `unittest`, you wrap these callbacks with | |
| 113 `expectAsyncN`; when using `scheduled_test`, you use [wrapAsync] or | |
| 114 [wrapFuture]. | |
| 115 | |
| 116 [wrapAsync] has two important functions. First, any errors that occur in it | |
| 117 will be passed into the [Schedule] instead of causing the whole test to | |
| 118 crash. They can then be handled by [Schedule.onException] and | |
| 119 [Schedule.onComplete]. Second, a task queue isn't considered finished until | |
| 120 all of its [wrapAsync]-wrapped functions have been called. This ensures that | |
| 121 [Schedule.onException] and [Schedule.onComplete] will always run after all | |
| 122 the test code in the main queue. | |
| 123 | |
| 124 Note that the [completes], [completion], and [throws] matchers use | |
| 125 [wrapAsync] internally, so they're safe to use in conjunction with scheduled | |
| 126 tests. | |
| 127 | |
| 128 Here's an example of a test using [wrapAsync] to catch errors thrown in the | |
| 129 callback of a fictional `startServer` function: | |
| 130 | |
| 131 ```dart | |
| 132 import 'package:scheduled_test/scheduled_test.dart'; | |
| 133 | |
| 134 void main() { | |
| 135 test('sendRequest sends a request', () { | |
| 136 startServer(wrapAsync((request) { | |
| 137 expect(request.body, equals('payload')); | |
| 138 request.response.close(); | |
| 139 })); | |
| 140 | |
| 141 schedule(() => sendRequest('payload')); | |
| 142 }); | |
| 143 } | |
| 144 ``` | |
| 145 | |
| 146 [wrapFuture] works similarly to [wrapAsync], but instead of wrapping a | |
| 147 single callback it wraps a whole [Future] chain. Like [wrapAsync], it | |
| 148 ensures that the task queue doesn't complete until the out-of-band chain has | |
| 149 finished, and that any errors in the chain are piped back into the scheduled | |
| 150 test. For example: | |
| 151 | |
| 152 ```dart | |
| 153 import 'package:scheduled_test/scheduled_test.dart'; | |
| 154 | |
| 155 void main() { | |
| 156 test('sendRequest sends a request', () { | |
| 157 wrapFuture(server.nextRequest.then((request) { | |
| 158 expect(request.body, equals('payload')); | |
| 159 expect(request.headers['content-type'], equals('text/plain')); | |
| 160 })); | |
| 161 | |
| 162 schedule(() => sendRequest('payload')); | |
| 163 }); | |
| 164 } | |
| 165 ``` | |
| 166 | |
| 167 ## Timeouts | |
| 168 | |
| 169 `scheduled_test` has a built-in timeout of 5 seconds (configurable via | |
| 170 [Schedule.timeout]). This timeout is aware of the structure of the schedule; | |
| 171 this means that it will reset for each task in a queue, when moving between | |
| 172 queues, or almost any other sort of interaction with [currentSchedule]. As | |
| 173 long as the [Schedule] knows your test is making some sort of progress, it | |
| 174 won't time out. | |
| 175 | |
| 176 If a single task might take a long time, you can also manually tell the | |
| 177 [Schedule] that it's making progress by calling [Schedule.heartbeat], which | |
| 178 will reset the timeout whenever it's called. | |
| 179 | |
| 180 [pub]: http://pub.dartlang.org | |
| 181 [pkg]: http://pub.dartlang.org/packages/scheduled_test | |
| OLD | NEW |