| [](https://github.com/dart-lang/test/actions/workflows/test_process.yaml) |
| [](https://pub.dev/packages/test_process) |
| [](https://pub.dev/packages/test_process/publisher) |
| |
| A package for testing subprocesses. |
| |
| This exposes a [`TestProcess`][TestProcess] class that wraps `dart:io`'s |
| [`Process`][Process] class and makes it easy to read standard output |
| line-by-line. `TestProcess` works the same as `Process` in many ways, but there |
| are a few major differences. |
| |
| [TestProcess]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess-class.html |
| [Process]: https://api.dart.dev/stable/dart-io/Process-class.html |
| |
| ## Standard Output |
| |
| `Process.stdout` and `Process.stderr` are binary streams, which is the most |
| general API but isn't the most helpful when working with a program that produces |
| plain text. Instead, [`TestProcess.stdout`][stdout] and |
| [`TestProcess.stderr`][stderr] emit a string for each line of output the process |
| produces. What's more, they're [`StreamQueue`][StreamQueue]s, which means |
| they provide a *pull-based API*. For example: |
| |
| [stdout]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/stdout.html |
| [stderr]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/stderr.html |
| [StreamQueue]: https://pub.dev/documentation/async/latest/async/StreamQueue-class.html |
| |
| ```dart |
| import 'package:test/test.dart'; |
| import 'package:test_process/test_process.dart'; |
| |
| void main() { |
| test('pub get gets dependencies', () async { |
| // TestProcess.start() works just like Process.start() from dart:io. |
| var process = await TestProcess.start('dart', ['pub', 'get']); |
| |
| // StreamQueue.next returns the next line emitted on standard out. |
| var firstLine = await process.stdout.next; |
| expect(firstLine, equals('Resolving dependencies...')); |
| |
| // Each call to StreamQueue.next moves one line further. |
| String next; |
| do { |
| next = await process.stdout.next; |
| } while (next != 'Got dependencies!'); |
| |
| // Assert that the process exits with code 0. |
| await process.shouldExit(0); |
| }); |
| } |
| ``` |
| |
| The `test` package's [stream matchers][] have built-in support for |
| `StreamQueues`, which makes them perfect for making assertions about a process's |
| output. We can use this to clean up the previous example: |
| |
| [stream matchers]: https://github.com/dart-lang/test#stream-matchers |
| |
| ```dart |
| import 'package:test/test.dart'; |
| import 'package:test_process/test_process.dart'; |
| |
| void main() { |
| test('pub get gets dependencies', () async { |
| var process = await TestProcess.start('dart', ['pub', 'get']); |
| |
| // Each stream matcher will consume as many lines as it matches from a |
| // StreamQueue, and no more, so it's safe to use them in sequence. |
| await expectLater(process.stdout, emits('Resolving dependencies...')); |
| |
| // The emitsThrough matcher matches and consumes any number of lines, as |
| // long as they end with one matching the argument. |
| await expectLater(process.stdout, emitsThrough('Got dependencies!')); |
| |
| await process.shouldExit(0); |
| }); |
| } |
| ``` |
| |
| If you want to access the standard output streams without consuming any values |
| from the queues, you can use the [`stdoutStream()`][stdoutStream] and |
| [`stderrStream()`][stderrStream] methods. Each time you call one of these, it |
| produces an entirely new stream that replays the corresponding output stream |
| from the beginning, regardless of what's already been produced by `stdout`, |
| `stderr`, or other calls to the stream method. |
| |
| [stdoutStream]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/stdoutStream.html |
| [stderrStream]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/stderrStream.html |
| |
| ## Signals and Termination |
| |
| The way signaling works is different from `dart:io` as well. `TestProcess` still |
| has a [`kill()`][kill] method, but it defaults to `SIGKILL` on Mac OS and Linux |
| to ensure (as best as possible) that processes die without leaving behind |
| zombies. If you want to send a particular signal (which is unsupported on |
| Windows), you can do so by explicitly calling [`signal()`][signal]. |
| |
| [kill]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/kill.html |
| [signal]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/signal.html |
| |
| In addition to [`exitCode`][exitCode], which works the same as in `dart:io`, |
| `TestProcess` also adds a new method named [`shouldExit()`][shouldExit]. This |
| lets tests wait for a process to exit, and (if desired) assert what particular |
| exit code it produced. |
| |
| [exitCode]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/exitCode.html |
| [shouldExit]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/shouldExit.html |
| |
| ## Debugging Output |
| |
| When a test using `TestProcess` fails, it will print all the output produced by |
| that process. This makes it much easier to figure out what went wrong and why. |
| The debugging output uses a header based on the process's invocation by |
| default, but you can pass in custom `description` parameters to |
| [`TestProcess.start()`][start] to control the headers. |
| |
| [start]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/start.html |
| |
| `TestProcess` will also produce debugging output as the test runs if you pass |
| `forwardStdio: true` to `TestProcess.start()`. This can be particularly useful |
| when you're using an interactive debugger and you want to figure out what a |
| process is doing before the test finishes and the normal debugging output is |
| printed. |