Long long ago in the dawning days of Dart, when
Futures were new and
Streams unknown, Bob saw the need for a package manager to bring the people together. Together he and I spun formless bits into code and wove that code into pub, which even today carries code into the eager hands of Dartisans. In those olden days, we had no modern marvels such as the twins
await, and our tests grew heavy and graceless under the weight of their
So it was that I came up with an idea for a new way of writing tests. Instead of gluing the steps together with the cumbersome
Future API, each step would register an asynchronous callback in a queue. Once all the steps were queued up, the library would run them in order. All the API boilerplate would be hidden away from the user, and the tests would look nice and clean. After honing this technique on pub’s tests, I brought it out into its own package, which I called
If this whole idea of queuing up tasks and running them later sounds complicated, that’s because it was.
scheduled_test did a great job of pushing complexity away from test cases, but in return any infrastructure that ran those test cases got much more complex. You couldn’t just write normal Dart code, you had to make sure to wrap everything in
schedule() and allow users to pass in
Futures and all sorts of hassle. I had created a better sort of tests, but not one without flaws.
The Beginning of the End
Then in Dart 1.9, support for
await landed and completely changed the equation. No longer did asynchronous APIs have to involve a mess of nesting and callbacks—even the most
Future-heavy tests were free to look like plain old synchronous code with some extra keywords sprinkled on top. The driving purpose of
scheduled_test, making asynchronous tests look simple and comprehensible, was now supported by the language itself.
You may then wonder, why didn’t
scheduled_test fade into obscurity and disuse fifteen Dart versions ago? You may ask, “Natalie, when you created the new
test package, why did you decide to use
scheduled_test when it no longer had a purpose?” The answer to these questions is infrastructure.
Over the years we had built up a number of APIs around the core functionality of
scheduled_test, and even when that functionality got less relevant the APIs that used it were very useful. We couldn’t migrate old code until we had a good replacement, and even for new code
scheduled_test was still the best option for testing files and subprocesses.
The Final Blow
This year2, one of my goals was to finally rid the world of
scheduled_test once and for all. This meant buckling down and replacing all the infrastructure we’d built on it over the years with new packages that use nothing more than
dart:async and the base
test API. It took a lot of work, but I did it. I’m pleased to say that pub no longer uses
scheduled_test, nor does
test. These were
scheduled_test’s biggest users, and they serve as a proof-of-concept for the suitability of its replacements:
scheduled_test/descriptorlibrary, which provided a simple and readable API for creating and validating files and directories, is replaced by the
scheduled_test/scheduled_processlibrary, which wrapped
Processlibrary to provide better debugging and easier line-by-line access to the standard IO streams, is replaced by the
scheduled_test/scheduled_streamlibrary provided a pull-based API for streams which has been replaced by the
StreamQueueclass, and the stream matchers have been replaced by a suite of matchers in
scheduled_test/scheduled_serverlibrary, which provided a server that validates and handles individual requests, has been moved to the
The core of
scheduled_testmay be going away, but it had a few useful features that I salvaged. The
testpackage can now print a message only when a test fails and add tear-downs at runtime, both of which were originally supported in
I hope these APIs can be used more widely now that they’re no longer tangled up with
scheduled_test. In fact, I encourage you to try them out right away if they seem useful—or write your own libraries if they don’t!